├── .eslintrc ├── .gitignore ├── .npmignore ├── README.md ├── example ├── basic.jsx ├── example.config.js ├── index.html └── server.config.js ├── mocha.opts ├── package.json ├── src ├── head.jsx ├── rows.jsx ├── table.jsx └── uniqueId.js └── tests ├── head.jsx ├── setup.js └── table.jsx /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "es6": true 5 | }, 6 | "ecmaFeatures": { 7 | "blockBindings": true, 8 | "forOf": true, 9 | "jsx": true, 10 | "modules": true 11 | }, 12 | "rules": { 13 | "indent": [2, 2], 14 | "max-len": 0, 15 | "semi": 0, 16 | "quotes": 0, 17 | "no-console": 0, 18 | "no-trailing-spaces": 0, 19 | "curly": 0, 20 | "camelcase": 0, 21 | "react/jsx-boolean-value": 1, 22 | "react/jsx-quotes": 1, 23 | "react/jsx-no-undef": 1, 24 | "react/jsx-uses-react": 1, 25 | "react/jsx-uses-vars": 1, 26 | "react/no-did-mount-set-state": 1, 27 | "react/no-did-update-set-state": 1, 28 | "react/no-multi-comp": 1, 29 | "react/no-unknown-property": 1, 30 | "react/prop-types": 1, 31 | "react/react-in-jsx-scope": 1, 32 | "react/self-closing-comp": 1, 33 | "react/sort-comp": 1, 34 | "react/wrap-multilines": 1, 35 | "new-cap": [1, {newIsCap: true, capIsNew: false}], 36 | }, 37 | "plugins": [ 38 | "react" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | rethinkdb_data 3 | npm-debug.log 4 | example/main.js 5 | .DS_Store 6 | lib 7 | # Ignore all vim swap files 8 | *.swp 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | tests 3 | .eslintrc 4 | mocha.opts 5 | circle.yml 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##Install 2 | `npm install react-legit-table` 3 | 4 | ##Another Table Component.... 5 | 6 | I know what you're thinking... Yet another one of these things, right? I was using Facebook's Fixed Data Table until I realized how big of a library it is. All I needed was a simple html table. It's as simple as it can be, and works great with bootstrap: 7 | 8 | ###Importing and setup 9 | ~~~js 10 | import Table from 'react-legit-table' 11 | 12 | let rows = [ 13 | { 14 | id: 1, 15 | name: 'Zach', 16 | job: 'coding' 17 | }, 18 | { 19 | id: 2, 20 | name: 'Jed', 21 | job: 'Being a boss' 22 | } 23 | ] 24 | ~~~ 25 | 26 | ###Rendering 27 | 28 | ~~~js 29 | render(){ 30 | return ( 31 | 32 | ) 33 | } 34 | ~~~ 35 | 36 | The component expects simple, regular ol' javascript objects inside of an array. 37 | 38 | ###Options 39 | 40 | `modify` accepts an object with keys equal to those in your rows object and the values are callbacks that will be called on rendering a row. 41 | Optionally, use `modifyAll` to change every item. 42 | 43 | **Example:** 44 | 45 | ~~~js 46 | 47 | modifyId({hidden, value, key, row}){ 48 | return {value} 49 | } 50 | 51 | render() { 52 | return ( 53 |
58 | ); 59 | } 60 | 61 | //optionally modifyAll rows: 62 | 63 |
67 | ~~~ 68 | 69 | `capitalize` Optionally, turn off capitalization of header row. True by default. 70 | 71 | ~~~js 72 |
73 | ~~~ 74 | 75 | ##Requests? 76 | 77 | I want to keep this simple and it has been perfect for my use case so far, but I'd love to hear suggestions. 78 | -------------------------------------------------------------------------------- /example/basic.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Table from '../src/table'; 3 | 4 | var rows = [ 5 | { 6 | id: 1, 7 | name: 'Zach', 8 | job: 'coding' 9 | }, 10 | { 11 | id: 2, 12 | name: 'Jed', 13 | job: 'Being a boss' 14 | } 15 | ]; 16 | 17 | export default class Basic extends React.Component { 18 | modifyId(id){ 19 | return `the user's is ${id}` 20 | } 21 | render() { 22 | return ( 23 |
29 | ); 30 | } 31 | } 32 | 33 | React.render(, document.getElementById('react')); 34 | -------------------------------------------------------------------------------- /example/example.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var path = require('path'); 3 | 4 | module.exports = { 5 | entry: './example/basic.jsx', 6 | output: { 7 | path: __dirname, 8 | filename: "[name].js" 9 | }, 10 | resolve: { 11 | extensions: ['', '.js', '.jsx', '.es6'], 12 | modulesDirectories: ['node_modules'] 13 | }, 14 | module: { 15 | loaders: [ 16 | { test: /\.jsx$|\.es6$|\.js$/, loaders: ['babel-loader?stage=0'], exclude: /node_modules/ }, 17 | ] 18 | }, 19 | plugins: [ 20 | new webpack.NoErrorsPlugin() 21 | ], 22 | devtool: "eval-source-map" 23 | }; 24 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Basic Example 4 | 5 | 6 | 7 |
8 |
9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/server.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var path = require('path'); 3 | 4 | module.exports = { 5 | entry: { 6 | 'basic': [ 7 | 'webpack-dev-server/client?http://localhost:8881/', 8 | 'webpack/hot/only-dev-server', 9 | './example/basic.jsx' 10 | ] 11 | }, 12 | output: { 13 | path: __dirname, 14 | filename: "[name].js", 15 | publicPath: 'http://localhost:8881/', 16 | chunkFilename: '[id].chunk.js', 17 | sourceMapFilename: '[name].map' 18 | }, 19 | resolve: { 20 | extensions: ['', '.js', '.jsx', '.es6'], 21 | modulesDirectories: ['node_modules'] 22 | }, 23 | module: { 24 | loaders: [ 25 | { test: /\.jsx$|\.es6$|\.js$/, loaders: ['react-hot', 'babel-loader?stage=0'], exclude: /node_modules/ }, 26 | { test: /\.scss$|\.css$/, loader: 'style-loader!style!css!sass' }, 27 | { test: /\.(jpe?g|png|gif)$/i, loader: 'url?limit=10000!img?progressive=true' } 28 | ] 29 | }, 30 | plugins: [ 31 | new webpack.NoErrorsPlugin() 32 | ], 33 | devtool: "eval-source-map" 34 | }; 35 | -------------------------------------------------------------------------------- /mocha.opts: -------------------------------------------------------------------------------- 1 | --require ./tests/setup 2 | --full-trace 3 | --compilers js:babel/register 4 | --recursive ./tests/**/*.jsx 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-legit-table", 3 | "version": "0.5.0", 4 | "description": "the simplest table component out there", 5 | "main": "lib/table.js", 6 | "scripts": { 7 | "compile": "babel src --stage 0 --out-dir lib;", 8 | "prepublish": "babel src --stage 0 --out-dir lib;", 9 | "dev-server": "webpack-dev-server --config ./example/server.config.js --hot --port 8881", 10 | "example": "webpack --config ./example/example.config.js", 11 | "test": "mocha --opts ./mocha.opts; eslint ./src/ --ext .jsx,.js --global require,exports:true" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+ssh://git@github.com/legitcode/table.git" 16 | }, 17 | "keywords": [ 18 | "react-component", 19 | "react", 20 | "table", 21 | "react-table", 22 | "bootstrap" 23 | ], 24 | "author": "Zach Silveira", 25 | "license": "ISC", 26 | "bugs": { 27 | "url": "https://github.com/legitcode/table/issues" 28 | }, 29 | "homepage": "https://github.com/legitcode/table#readme", 30 | "devDependencies": { 31 | "babel": "^5.8.23", 32 | "babel-core": "^5.8.23", 33 | "babel-eslint": "^4.1.1", 34 | "babel-loader": "^5.3.2", 35 | "chai": "^3.2.0", 36 | "eslint": "^0.24.1", 37 | "eslint-plugin-react": "^2.7.0", 38 | "expect": "^1.8.0", 39 | "legit-tests": "^0.6.0", 40 | "mocha": "^2.2.5", 41 | "mocha-babel": "^3.0.0", 42 | "react-hot-loader": "^1.3.0", 43 | "webpack": "^1.12.1", 44 | "webpack-dev-server": "^1.10.1" 45 | }, 46 | "dependencies": { 47 | "react": "^0.14.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/head.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import uniqueId from './uniqueId' 3 | 4 | export default class Head extends React.Component{ 5 | static propTypes = { 6 | row: React.PropTypes.object.isRequired, 7 | hide: React.PropTypes.array, 8 | capitalize: React.PropTypes.bool 9 | } 10 | 11 | static defaultProps = { 12 | capitalize: true 13 | } 14 | 15 | headings() { 16 | let row = Object.assign({}, this.props.row) 17 | return Object.keys(row).map((name) => { 18 | return
19 | }) 20 | } 21 | 22 | titleize(str) { 23 | let words = str.replace(/([A-Z])/g, ' $1').replace(/_/g, ' ').split(' ') 24 | 25 | let array = words.map((word) => { 26 | return `${word.charAt(0).toUpperCase()}${word.substring(1)}` 27 | }) 28 | 29 | return array.join(' ').trim() 30 | } 31 | 32 | render() { 33 | return ( 34 | 35 | 36 | {this.headings()} 37 | 38 | 39 | ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/rows.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import uniqueId from './uniqueId' 3 | 4 | export default class Rows extends React.Component{ 5 | 6 | static propTypes = { 7 | rows: React.PropTypes.array, 8 | modify: React.PropTypes.object, 9 | hide: React.PropTypes.array, 10 | modifyAll: React.PropTypes.func 11 | } 12 | 13 | rows(){ 14 | let rows = []; 15 | 16 | for(let row of this.props.rows){ 17 | let rowList = [] 18 | let id = row[Object.keys(row)[0]] 19 | 20 | for(let item in row){ 21 | let value = row[item] 22 | let params = { 23 | value: row[item], 24 | key: item, 25 | row 26 | } 27 | if(this.props.modifyAll) value = this.props.modifyAll(params) 28 | else if(this.props.modify[item]) value = this.props.modify[item](params) 29 | 30 | rowList.push() 31 | } 32 | 33 | rows.push({rowList}) 34 | } 35 | 36 | return rows; 37 | } 38 | 39 | render(){ 40 | return ( 41 | 42 | {this.rows()} 43 | 44 | ) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/table.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Head from './head' 3 | import Rows from './rows' 4 | 5 | export default class Table extends React.Component{ 6 | static propTypes = { 7 | rows: React.PropTypes.array.isRequired, 8 | capitalize: React.PropTypes.bool, 9 | modify: React.PropTypes.object 10 | 11 | } 12 | 13 | render() { 14 | if (this.props.rows.length === 0) return null 15 | 16 | let { 17 | rows, 18 | capitalize, 19 | modify, 20 | ...attributes 21 | } = this.props 22 | 23 | return ( 24 |
{this.props.capitalize ? this.titleize(name) : name}
{value}
25 | 26 | 27 |
28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/uniqueId.js: -------------------------------------------------------------------------------- 1 | var number = 0 2 | export default function uniqueId(id){ 3 | number++ 4 | return number + '-' + id 5 | } 6 | -------------------------------------------------------------------------------- /tests/head.jsx: -------------------------------------------------------------------------------- 1 | import Test from 'legit-tests' 2 | import Head from '../src/head' 3 | import { expect } from 'chai' 4 | 5 | /* globals describe, it */ 6 | 7 | describe('Head component', () => { 8 | describe('initialize', () => { 9 | it('should default capitalize to true', () => { 10 | Test() 11 | .test(({instance}) => { 12 | expect(instance.props.capitalize).to.be.true 13 | }) 14 | }) 15 | 16 | it('should set capitalize to false if it is passed in', () => { 17 | Test() 18 | .test(({instance}) => { 19 | expect(instance.props.capitalize).to.be.false 20 | }) 21 | }) 22 | }) 23 | 24 | describe('#headings', () => { 25 | var head = new Head({ row: { foo: "bar" } }) 26 | 27 | it('should return an array of header columns', () => { 28 | let headings = head.headings() 29 | 30 | expect(headings.length).to.equal(1) 31 | expect(headings[0].type).to.equal('th') 32 | expect(headings[0].key).to.equal('3-foo') 33 | }) 34 | }) 35 | 36 | describe('#titleize', () => { 37 | var head = new Head() 38 | 39 | it('should titleize a string properly', () => { 40 | expect(head.titleize('foo bar')).to.equal('Foo Bar') 41 | }) 42 | 43 | it('should titleize a snake case string properly', () => { 44 | expect(head.titleize('foo_bar')).to.equal('Foo Bar') 45 | }) 46 | 47 | it('should titleize a camel cased string properly', () => { 48 | expect(head.titleize('fooBar')).to.equal('Foo Bar') 49 | }) 50 | 51 | it('should titleize a constantized string properly', () => { 52 | expect(head.titleize('FooBar')).to.equal('Foo Bar') 53 | }) 54 | }) 55 | }) 56 | -------------------------------------------------------------------------------- /tests/setup.js: -------------------------------------------------------------------------------- 1 | require("babel/register")({ 2 | stage: 0 3 | }); 4 | -------------------------------------------------------------------------------- /tests/table.jsx: -------------------------------------------------------------------------------- 1 | import Test from 'legit-tests' 2 | import Table from '../src/table' 3 | import Head from '../src/head' 4 | import Rows from '../src/rows' 5 | import { expect } from 'chai' 6 | 7 | /* globals describe, it */ 8 | 9 | describe('Table component', () => { 10 | var table = new Table({ rows: [] }) 11 | 12 | describe('#render', () => { 13 | it('should not render the component if the rows are empty', () => { 14 | Test(, {shallow: true}) 15 | .test(({instance}) => { 16 | expect(instance).to.be.null 17 | }) 18 | }) 19 | 20 | it('should render the header and rows if the rows are not empty', () => { 21 | let rows = [{foo: "1"}] 22 | 23 | Test(
, {shallow: true}) 24 | .test(({instance}) => { 25 | let head = instance.props.children[0], 26 | tableRows = instance.props.children[1] 27 | expect(head.props).to.not.be.undefined 28 | expect(tableRows.props).to.not.be.undefined 29 | expect(head.props.row).to.equal(rows[0]) 30 | expect(tableRows.props.rows).to.equal(rows) 31 | }) 32 | }) 33 | 34 | it('should correctly modify row', () => { 35 | let modify = ({value}) => { 36 | return `Name: ${value}` 37 | } 38 | 39 | Test(
) 40 | .find('td') 41 | .element(td => { 42 | expect(td.props.children).to.be.equal('Name: zach') 43 | }) 44 | }) 45 | 46 | it('should correctly modify all rows', () => { 47 | let modify = ({value}) => { 48 | return `yo ${value}` 49 | } 50 | 51 | Test(
) 52 | .find('td') 53 | .element(td => { 54 | expect(td[0].props.children).to.be.equal('yo zach') 55 | expect(td[1].props.children).to.be.equal('yo awesome') 56 | }) 57 | }) 58 | }) 59 | }) 60 | --------------------------------------------------------------------------------