├── .babelrc
├── .eslintrc.json
├── .gitignore
├── .mochasetup.js
├── .travis.yml
├── LICENSE
├── README.md
├── example
├── app
│ ├── index.html
│ └── main.css
└── src
│ ├── Main.js
│ └── app.js
├── gulpfile.js
├── package.json
├── src
├── DataTables
│ ├── DataTables.js
│ ├── DataTablesHeaderColumn.js
│ ├── DataTablesHeaderToolbar.js
│ ├── DataTablesRow.js
│ ├── DataTablesRowColumn.js
│ ├── DataTablesTable.js
│ ├── DataTablesTableBody.js
│ └── index.js
└── index.js
├── test
└── DataTables
│ ├── DataTables.spec.js
│ └── tableSettings.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015", "stage-1"]
3 | }
4 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "es6": true,
4 | "browser": true,
5 | "mocha": true,
6 | "node": true
7 | },
8 | "parser": "babel-eslint",
9 | "parserOptions": {
10 | "ecmaVersion": 7,
11 | "sourceType": "module",
12 | "ecmaFeatures": {
13 | "jsx": true,
14 | "experimentalObjectRestSpread": true
15 | }
16 | },
17 | "globals": {
18 | "React": true
19 | },
20 | "plugins": [
21 | "babel",
22 | "react"
23 | ],
24 | "extends": [
25 | "eslint:recommended"
26 | ],
27 | "rules": {
28 | "array-bracket-spacing": ["error", "never"],
29 | "arrow-spacing": "error",
30 | "arrow-parens": "error",
31 | "brace-style": "error",
32 | "comma-dangle": ["error", "always-multiline"],
33 | "comma-spacing": ["error", {"before": false, "after": true}],
34 | "comma-style": ["error", "last"],
35 | "computed-property-spacing": ["error", "never"],
36 | "consistent-this": ["error", "self"],
37 | "consistent-return": "off",
38 | "dot-notation": "error",
39 | "dot-location": ["error", "property"],
40 | "eqeqeq": ["error", "smart"],
41 | "eol-last": "error",
42 | "indent": ["error", 2, {"SwitchCase": 1}],
43 | "id-blacklist": ["error", "e"],
44 | "jsx-quotes": ["error", "prefer-double"],
45 | "keyword-spacing": "error",
46 | "key-spacing": "error",
47 | "max-len": ["error", 120, 4],
48 | "new-cap": ["off", {"capIsNew": true, "newIsCap": true}],
49 | "no-unused-expressions": "error",
50 | "no-unused-vars": "error",
51 | "no-shadow": "off",
52 | "no-spaced-func": "error",
53 | "no-multiple-empty-lines": "error",
54 | "no-multi-spaces": "error",
55 | "no-undef": "error",
56 | "no-empty-pattern": "error",
57 | "no-dupe-keys": "error",
58 | "no-dupe-args": "error",
59 | "no-duplicate-case": "error",
60 | "no-cond-assign": "error",
61 | "no-extra-semi": "error",
62 | "no-extra-boolean-cast": "error",
63 | "no-trailing-spaces": "error",
64 | "no-underscore-dangle": "error",
65 | "no-unneeded-ternary": "error",
66 | "no-unreachable": "error",
67 | "no-var": "error",
68 | "one-var": ["error", "never"],
69 | "operator-linebreak": ["error", "after"],
70 | "padded-blocks": ["error", "never"],
71 | "prefer-arrow-callback": "off",
72 | "prefer-const": "error",
73 | "prefer-template": "error",
74 | "quotes": ["error", "single", "avoid-escape"],
75 | "semi": ["error", "always"],
76 | "space-before-blocks": ["error", "always"],
77 | "space-before-function-paren": ["error", "never"],
78 | "space-infix-ops": "error",
79 | "space-unary-ops": ["error", {"words": true, "nonwords": false}],
80 | "spaced-comment": "error",
81 | "yoda": "error",
82 | "babel/object-curly-spacing": ["error", "never"],
83 | "babel/generator-star-spacing": "error",
84 | "babel/array-bracket-spacing": "error",
85 | "babel/arrow-parens": "error",
86 | "babel/no-await-in-loop": "error",
87 | "babel/func-params-comma-dangle": "error",
88 | "babel/flow-object-type": "error",
89 | "react/display-name": "error",
90 | "react/jsx-boolean-value": ["error", "always"],
91 | "react/jsx-closing-bracket-location": "error",
92 | "react/jsx-curly-spacing": "error",
93 | "react/jsx-equals-spacing": "error",
94 | "react/jsx-filename-extension": ["error", {"extensions": [".js"]}],
95 | "react/jsx-first-prop-new-line": ["error", "multiline"],
96 | "react/jsx-handler-names": "error",
97 | "react/jsx-indent": ["error", 2],
98 | "react/jsx-indent-props": ["error", 2],
99 | "react/jsx-max-props-per-line": ["error", {"maximum": 3}],
100 | "react/jsx-no-comment-textnodes": "error",
101 | "react/jsx-no-duplicate-props": "error",
102 | "react/jsx-no-undef": "error",
103 | "react/jsx-pascal-case": "error",
104 | "react/jsx-space-before-closing": "error",
105 | "react/jsx-uses-react": "error",
106 | "react/jsx-uses-vars": "error",
107 | "react/jsx-wrap-multilines": "error",
108 | "react/no-danger": "error",
109 | "react/no-deprecated": "error",
110 | "react/no-did-mount-set-state": "error",
111 | "react/no-did-update-set-state": "error",
112 | "react/no-direct-mutation-state": "error",
113 | "react/no-multi-comp": "off",
114 | "react/no-render-return-value": "error",
115 | "react/no-is-mounted": "error",
116 | "react/no-unknown-property": "error",
117 | "react/prefer-arrow-callback": "off",
118 | "react/prefer-es6-class": "error",
119 | "react/prop-types": "error",
120 | "react/react-in-jsx-scope": "error",
121 | "react/require-extension": "error",
122 | "react/require-render-return": "error",
123 | "react/self-closing-comp": "error",
124 | "react/sort-comp": "error",
125 | "react/sort-prop-types": "error",
126 | "react/no-string-refs": "warn",
127 |
128 | "strict": "off",
129 | "no-case-declarations": "off",
130 | "react/jsx-key": "off",
131 | "react/jsx-no-bind": "off",
132 | "react/jsx-no-literals": "off",
133 | "react/jsx-no-target-blank": "off",
134 | "react/jsx-sort-props": "off",
135 | "react/no-set-state": "off",
136 | "react/forbid-prop-types": "off",
137 | "react/prefer-stateless-function": "off",
138 | "react/require-optimization": "off",
139 | "babel/object-shorthand": "off",
140 | "babel/new-cap": "off"
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.log
3 | node_modules/
4 | example/app/bundle.js
5 | build
6 | coverage/
7 | .nyc_output
8 | ### Node template
9 | # Logs
10 | logs
11 | *.log
12 | npm-debug.log*
13 | yarn-debug.log*
14 | yarn-error.log*
15 |
16 | # Runtime data
17 | pids
18 | *.pid
19 | *.seed
20 | *.pid.lock
21 |
22 | # Directory for instrumented libs generated by jscoverage/JSCover
23 | lib-cov
24 |
25 | # Coverage directory used by tools like istanbul
26 | coverage
27 |
28 | # nyc test coverage
29 | .nyc_output
30 |
31 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
32 | .grunt
33 |
34 | # Bower dependency directory (https://bower.io/)
35 | bower_components
36 |
37 | # node-waf configuration
38 | .lock-wscript
39 |
40 | # Compiled binary addons (http://nodejs.org/api/addons.html)
41 | build/Release
42 |
43 | # Dependency directories
44 | node_modules/
45 | jspm_packages/
46 |
47 | # Typescript v1 declaration files
48 | typings/
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Optional REPL history
57 | .node_repl_history
58 |
59 | # Output of 'npm pack'
60 | *.tgz
61 |
62 | # Yarn Integrity file
63 | .yarn-integrity
64 |
65 | # dotenv environment variables file
66 | .env
67 |
68 | ### Example user template template
69 | ### Example user template
70 |
71 | # IntelliJ project files
72 | .idea
73 | *.iml
74 | out
75 | gen
76 |
--------------------------------------------------------------------------------
/.mochasetup.js:
--------------------------------------------------------------------------------
1 | require('babel-register')();
2 |
3 | var jsdom = require('jsdom').jsdom;
4 |
5 | var exposedProperties = ['window', 'navigator', 'document'];
6 |
7 | global.document = jsdom('');
8 | global.window = document.defaultView;
9 | Object.keys(document.defaultView).forEach((property) => {
10 | if (typeof global[property] === 'undefined') {
11 | exposedProperties.push(property);
12 | global[property] = document.defaultView[property];
13 | }
14 | });
15 |
16 | global.navigator = {
17 | userAgent: 'node.js'
18 | };
19 |
20 | documentRef = document;
21 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "node"
4 | before_script:
5 | - npm install -g gulp
6 | script:
7 | - npm run lint & npm test
8 | after_success:
9 | - npm run test:coveralls
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Hyojin Kwak
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 NONINFRINGEMENT. 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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Material-UI-Datatables
2 |
3 | [](https://www.npmjs.com/package/material-ui-datatables)
4 | [](https://travis-ci.org/hyojin/material-ui-datatables)
5 | [](https://coveralls.io/github/hyojin/material-ui-datatables?branch=master)
6 |
7 | An another React Data tables component.
8 | Material-UI-Datatables is a custom [React](https://facebook.github.io/react/) component using awesome [Material-UI](http://www.material-ui.com/). It provides rendering data and emitting events
9 | such as filter and column sort and pagination which may help you dealing with your data. But it doesn't provide features all done within the component. Most parts of this component are stateless, which means you need to implement your logic for the events.
10 |
11 | **Now material-ui provides [example code](https://material-ui-1dab0.firebaseapp.com/demos/tables/) of data tables component with it's v1.0.0 package**
12 |
13 | ## Installation
14 | ```sh
15 | npm install material-ui-datatables
16 | ```
17 | or
18 | ```sh
19 | yarn add material-ui-datatables
20 | ```
21 |
22 | ## Demo
23 | [Demo](https://hyojin.github.io/material-ui-datatables/)
24 |
25 | ## Status
26 | Work in progress
27 |
28 | ## Usage
29 | ```jsx
30 | import React, {Component} from 'react';
31 | import DataTables from 'material-ui-datatables';
32 |
33 | const TABLE_COLUMNS = [
34 | {
35 | key: 'name',
36 | label: 'Dessert (100g serving)',
37 | }, {
38 | key: 'calories',
39 | label: 'Calories',
40 | },
41 | ...
42 | ];
43 |
44 | const TABLE_DATA = [
45 | {
46 | name: 'Frozen yogurt',
47 | calories: '159',
48 | fat: '6.0',
49 | carbs: '24',
50 | ...
51 | }, {
52 | name: 'Ice cream sandwich',
53 | calories: '159',
54 | fat: '6.0',
55 | carbs: '24',
56 | ...
57 | },
58 | ...
59 | ];
60 |
61 | class MyComponent extends Component {
62 | ...
63 | handleFilterValueChange = (value) => {
64 | // your filter logic
65 | }
66 |
67 | handleSortOrderChange = (key, order) => {
68 | // your sort logic
69 | }
70 |
71 | render() {
72 | return (
73 |
87 | );
88 | }
89 | }
90 | ```
91 |
92 | ## Properties
93 | | Name | Type | Default | Description |
94 | |----------------------|-----------|-------------------|----------------------------------------------|
95 | | columns | array | | Array of column settings [object](https://github.com/hyojin/material-ui-datatables#column-settings) |
96 | | count | number | 0 | |
97 | | data | array | | |
98 | | enableSelectAll | bool | false | |
99 | | filterHintText | string | 'Search' | |
100 | | filterValue | string | '' | |
101 | | footerToolbarStyle | object | | |
102 | | headerToolbarMode | string | 'default' | 'default' or 'filter' |
103 | | height | string | 'inherit' | |
104 | | initialSort | object | | {column: 'column key', order: 'asc or desc'} |
105 | | multiSelectable | bool | false | |
106 | | onCellClick | function | | |
107 | | onCellDoubleClick | function | | |
108 | | onFilterValueChange | function | | Should set 'showHeaderToolbar' to true first |
109 | | onNextPageClick | function | | |
110 | | onPreviousPageClick | function | | |
111 | | onRowSelection | function | | |
112 | | onRowSizeChange | function | | |
113 | | onSortOrderChange | function | | |
114 | | page | number | 1 | |
115 | | rowSize | number | 10 | |
116 | | rowSizeLabel | string | 'Rows per page:' | |
117 | | rowSizeList | array | [10, 30, 50, 100] | |
118 | | selectable | bool | false | |
119 | | selectedRows | array | [] | |
120 | | showCheckboxes | bool | false | |
121 | | showFooterToolbar | bool | true | |
122 | | showHeaderToolbar | bool | false | |
123 | | showHeaderToolbarFilterIcon | bool | true | |
124 | | showRowHover | bool | false | |
125 | | showRowSizeControls | bool | false | |
126 | | summaryLabelTemplate | function | | |
127 | | tableBodyStyle | object | | |
128 | | tableHeaderColumnStyle | object | | |
129 | | tableHeaderStyle | object | | |
130 | | tableRowColumnStyle | object | | |
131 | | tableRowStyle | object | | |
132 | | tableStyle | object | | |
133 | | tableWrapperStyle | object | | |
134 | | title | string | | Should set 'showHeaderToolbar' to true first |
135 | | titleStyle | object | | Should set 'showHeaderToolbar' to true first |
136 | | toolbarIconRight | node | | Can be an array of IconButton nodes |
137 |
138 | ## Column settings
139 | | Name | Type | Default | Description |
140 | |----------------------|-----------|-------------------|----------------------------------------------|
141 | | key | string | | |
142 | | label | string | | |
143 | | sortable | bool | false | |
144 | | tooltip | string | | |
145 | | className | string | | |
146 | | render | function | | |
147 | | alignRight | bool | | |
148 | | style | object | | Inline column styles |
149 |
150 | ### Setting example
151 | ```javascript
152 | {
153 | key: 'name',
154 | label: 'Dessert (100g serving)',
155 | sortable: true,
156 | tooltip: 'Dessert (100g serving)',
157 | className: 'important-column',
158 | style: {
159 | width: 250,
160 | },
161 | render: (name, all) =>
{name}
162 | }
163 | ```
164 |
165 | ## License
166 | MIT
167 |
--------------------------------------------------------------------------------
/example/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Material-UI Custom Components Example
7 |
8 |
9 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/example/app/main.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-family: 'Roboto', sans-serif;
3 | }
4 |
5 | body {
6 | font-size: 13px;
7 | line-height: 20px;
8 | }
9 |
--------------------------------------------------------------------------------
/example/src/Main.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import RaisedButton from 'material-ui/RaisedButton';
3 | import Dialog from 'material-ui/Dialog';
4 | import {deepOrange500} from 'material-ui/styles/colors';
5 | import FlatButton from 'material-ui/FlatButton';
6 | import getMuiTheme from 'material-ui/styles/getMuiTheme';
7 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
8 | import MenuItem from 'material-ui/MenuItem';
9 | import {Card, CardHeader} from 'material-ui/Card';
10 | import IconButton from 'material-ui/IconButton';
11 | import PersonAdd from 'material-ui/svg-icons/social/person-add';
12 | import InfoOutline from 'material-ui/svg-icons/action/info-outline';
13 |
14 | import DataTables from '../../src';
15 |
16 | const styles = {
17 | container: {
18 | textAlign: 'center',
19 | },
20 | component: {
21 | margin: '60px 20px',
22 | },
23 | titleStyle: {
24 | fontSize: 16,
25 | color: deepOrange500,
26 | },
27 | footerToolbarStyle: {
28 | padding: '0 100px',
29 | },
30 | tableStyle: {
31 | tableLayout: 'auto',
32 | },
33 | tableBodyStyle: {
34 | overflowX: 'auto',
35 | },
36 | tableWrapperStyle: {
37 | padding: 5,
38 | },
39 | };
40 |
41 | const muiTheme = getMuiTheme({
42 | palette: {
43 | accent1Color: deepOrange500,
44 | },
45 | });
46 |
47 | const TABLE_COLUMNS = [
48 | {
49 | key: 'name',
50 | label: 'Dessert (100g serving)',
51 | }, {
52 | key: 'calories',
53 | label: 'Calories',
54 | }, {
55 | key: 'fat',
56 | label: 'Fat (g)',
57 | }, {
58 | key: 'carbs',
59 | label: 'Carbs (g)',
60 | }, {
61 | key: 'protein',
62 | label: 'Protein (g)',
63 | }, {
64 | key: 'sodium',
65 | label: 'Sodium (mg)',
66 | }, {
67 | key: 'calcium',
68 | label: 'Calcium (%)',
69 | }, {
70 | key: 'iron',
71 | label: 'Iron (%)',
72 | },
73 | ];
74 |
75 | const TABLE_COLUMNS_TOOLTIP = [
76 | {
77 | key: 'name',
78 | label: 'Dessert (100g serving)',
79 | tooltip: 'Dessert (100g serving)',
80 | }, {
81 | key: 'calories',
82 | label: 'Calories',
83 | tooltip: 'Calories',
84 | }, {
85 | key: 'fat',
86 | label: 'Fat (g)',
87 | tooltip: 'Fat (g)',
88 | }, {
89 | key: 'carbs',
90 | label: 'Carbs (g)',
91 | tooltip: 'Carbs (g)',
92 | }, {
93 | key: 'protein',
94 | label: 'Protein (g)',
95 | tooltip: 'Protein (g)',
96 | }, {
97 | key: 'sodium',
98 | label: 'Sodium (mg)',
99 | tooltip: 'Sodium (mg)',
100 | }, {
101 | key: 'calcium',
102 | label: 'Calcium (%)',
103 | tooltip: 'Calcium (%)',
104 | }, {
105 | key: 'iron',
106 | label: 'Iron (%)',
107 | tooltip: 'Iron (%)',
108 | },
109 | ];
110 |
111 | const TABLE_COLUMNS_SORT_STYLE = [
112 | {
113 | key: 'name',
114 | label: 'Dessert (100g serving)',
115 | sortable: true,
116 | style: {
117 | width: 250,
118 | }
119 | }, {
120 | key: 'calories',
121 | label: 'Calories',
122 | sortable: true,
123 | }, {
124 | key: 'fat',
125 | label: 'Fat (g)',
126 | alignRight: true,
127 | }, {
128 | key: 'carbs',
129 | label: 'Carbs (g)',
130 | }, {
131 | key: 'protein',
132 | label: 'Protein (g)',
133 | }, {
134 | key: 'sodium',
135 | label: 'Sodium (mg)',
136 | }, {
137 | key: 'calcium',
138 | label: 'Calcium (%)',
139 | }, {
140 | key: 'iron',
141 | label: 'Iron (%)',
142 | },
143 | ];
144 |
145 | const TABLE_COLUMNS_CLASSNAME = [
146 | {
147 | key: 'name',
148 | label: 'Dessert (100g serving)',
149 | className: 'important-column',
150 | }, {
151 | key: 'calories',
152 | label: 'Calories',
153 | className: 'important-column',
154 | }, {
155 | key: 'fat',
156 | label: 'Fat (g)',
157 | }, {
158 | key: 'carbs',
159 | label: 'Carbs (g)',
160 | }, {
161 | key: 'protein',
162 | label: 'Protein (g)',
163 | }, {
164 | key: 'sodium',
165 | label: 'Sodium (mg)',
166 | }, {
167 | key: 'calcium',
168 | label: 'Calcium (%)',
169 | }, {
170 | key: 'iron',
171 | label: 'Iron (%)',
172 | },
173 | ];
174 |
175 | const TABLE_DATA = [
176 | {
177 | name: 'Frozen yogurt',
178 | calories: '159',
179 | fat: '6.0',
180 | carbs: '24',
181 | protein: '4.0',
182 | sodium: '87',
183 | calcium: '14%',
184 | iron: '1%',
185 | }, {
186 | name: 'Ice cream sandwich',
187 | calories: '159',
188 | fat: '6.0',
189 | carbs: '24',
190 | protein: '4.0',
191 | sodium: '87',
192 | calcium: '14%',
193 | iron: '1%',
194 | }, {
195 | name: 'Eclair',
196 | calories: '159',
197 | fat: '6.0',
198 | carbs: '24',
199 | protein: '4.0',
200 | sodium: '87',
201 | calcium: '14%',
202 | iron: '1%',
203 | }, {
204 | name: 'Cupcake',
205 | calories: '159',
206 | fat: '6.0',
207 | carbs: '24',
208 | protein: '4.0',
209 | sodium: '87',
210 | calcium: '14%',
211 | iron: '1%',
212 | }, {
213 | name: 'Gingerbread',
214 | calories: '159',
215 | fat: '6.0',
216 | carbs: '24',
217 | protein: '4.0',
218 | sodium: '87',
219 | calcium: '14%',
220 | iron: '1%',
221 | }, {
222 | name: 'Jelly bean',
223 | calories: '159',
224 | fat: '6.0',
225 | carbs: '24',
226 | protein: '4.0',
227 | sodium: '87',
228 | calcium: '14%',
229 | iron: '1%',
230 | }, {
231 | name: 'Lollipop',
232 | calories: '159',
233 | fat: '6.0',
234 | carbs: '24',
235 | protein: '4.0',
236 | sodium: '87',
237 | calcium: '14%',
238 | iron: '1%',
239 | }, {
240 | name: 'Honeycomb',
241 | calories: '159',
242 | fat: '6.0',
243 | carbs: '24',
244 | protein: '4.0',
245 | sodium: '87',
246 | calcium: '14%',
247 | iron: '1%',
248 | }, {
249 | name: 'Donut',
250 | calories: '159',
251 | fat: '6.0',
252 | carbs: '24',
253 | protein: '4.0',
254 | sodium: '87',
255 | calcium: '14%',
256 | iron: '1%',
257 | }, {
258 | name: 'KitKat',
259 | calories: '159',
260 | fat: '6.0',
261 | carbs: '24',
262 | protein: '4.0',
263 | sodium: '87',
264 | calcium: '14%',
265 | iron: '1%',
266 | },
267 | ];
268 |
269 | const TABLE_DATA_NEXT = [
270 | {
271 | name: 'Marshmallow',
272 | calories: '159',
273 | fat: '6.0',
274 | carbs: '24',
275 | protein: '4.0',
276 | sodium: '87',
277 | calcium: '14%',
278 | iron: '1%',
279 | },
280 | ];
281 |
282 | class Main extends Component {
283 | constructor(props, context) {
284 | super(props, context);
285 | this.handleSortOrderChange = this.handleSortOrderChange.bind(this);
286 | this.handleFilterValueChange = this.handleFilterValueChange.bind(this);
287 | this.handleCellClick = this.handleCellClick.bind(this);
288 | this.handleCellDoubleClick = this.handleCellDoubleClick.bind(this);
289 | this.handleRowSelection = this.handleRowSelection.bind(this);
290 | this.handlePreviousPageClick = this.handlePreviousPageClick.bind(this);
291 | this.handleNextPageClick = this.handleNextPageClick.bind(this);
292 | this.handlePersonAddClick = this.handlePersonAddClick.bind(this);
293 | this.handleInfoClick = this.handleInfoClick.bind(this);
294 |
295 | this.state = {
296 | data: TABLE_DATA,
297 | page: 1,
298 | };
299 | }
300 |
301 | handleSortOrderChange(key, order) {
302 | console.log('key:' + key + ' order: ' + order);
303 | }
304 |
305 | handleFilterValueChange(value) {
306 | console.log('filter value: ' + value);
307 | }
308 |
309 | handleCellClick(rowIndex, columnIndex, row, column) {
310 | console.log('rowIndex: ' + rowIndex + ' columnIndex: ' + columnIndex);
311 | }
312 |
313 | handleCellDoubleClick(rowIndex, columnIndex, row, column) {
314 | console.log('rowIndex: ' + rowIndex + ' columnIndex: ' + columnIndex);
315 | }
316 |
317 | handleRowSelection(selectedRows) {
318 | console.log('selectedRows: ' + selectedRows);
319 | }
320 |
321 | handlePreviousPageClick() {
322 | console.log('handlePreviousPageClick');
323 | this.setState({
324 | data: TABLE_DATA,
325 | page: 1,
326 | });
327 | }
328 |
329 | handleNextPageClick() {
330 | console.log('handleNextPageClick');
331 | this.setState({
332 | data: TABLE_DATA_NEXT,
333 | page: 2,
334 | });
335 | }
336 |
337 | handlePersonAddClick() {
338 | console.log('handlePersonAddClick');
339 | }
340 |
341 | handleInfoClick() {
342 | console.log('handleInfoClick');
343 | }
344 |
345 | render() {
346 | return (
347 |
348 |
349 |
Material-UI-Custom-Components
350 |
351 |
DataTables (Basic)
352 |
353 |
357 |
366 |
367 |
368 |
369 |
DataTables (Selectable & Tooltip & Pagination)
370 |
371 |
375 |
390 |
391 |
392 |
393 |
DataTables (Filter & Column Sort & Styled Column)
394 |
395 |
410 |
411 |
412 |
413 |
DataTables (Internationalization)
414 |
415 | {return `${start} - ${end} ${count}件`}}
425 | showCheckboxes={false}
426 | showHeaderToolbar={true}
427 | count={100}
428 | />
429 |
430 |
431 |
432 |
DataTables (Toolbar Icons & Styled title & Styled table)
433 |
434 |
453 |
454 | ,
455 |
458 |
459 |
460 | ]}
461 | />
462 |
463 |
464 |
465 |
DataTables (Column class name)
466 |
467 |
471 |
480 |
481 |
482 |
483 |
DataTables (Programmatically select rows)
484 |
485 |
489 |
500 |
501 |
502 |
503 |
DataTables (onRowSelection handler & onCellDoubleClick handler)
504 |
505 |
509 |
523 |
524 |
525 |
526 |
DataTables (Disable footer toolbar)
527 |
528 |
532 |
544 |
545 |
546 |
547 |
DataTables (Header toolbar mode)
548 |
549 |
565 |
566 |
567 |
568 |
DataTables (Hide filter icon)
569 |
570 |
585 |
586 |
587 |
588 |
589 | );
590 | }
591 | }
592 |
593 | export default Main;
594 |
--------------------------------------------------------------------------------
/example/src/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {render} from 'react-dom';
3 | import injectTapEventPlugin from 'react-tap-event-plugin';
4 | import Main from './Main';
5 |
6 | injectTapEventPlugin();
7 |
8 | render(, document.getElementById('root'));
9 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp');
2 | const browserify = require('browserify');
3 | const watchify = require('watchify');
4 | const babelify = require('babelify');
5 | const source = require('vinyl-source-stream');
6 | const eslint = require('gulp-eslint');
7 | const browserSync = require('browser-sync').create();
8 | const babel = require('gulp-babel');
9 | const del = require('del');
10 | const copy = require('gulp-copy');
11 | const mocha = require('gulp-mocha');
12 |
13 | function map_error(err) {
14 | console.log('Error : ' + err.message);
15 | this.emit('end');
16 | };
17 |
18 | function bundle_js(bundler) {
19 | return bundler.bundle()
20 | .on('error', map_error)
21 | .pipe(source('bundle.js'))
22 | .pipe(gulp.dest('./example/app'));
23 | };
24 |
25 | gulp.task('watchify', () => {
26 | const bundler = watchify(browserify('./example/src/app.js', Object.assign(watchify.args, { debug: true }))
27 | .transform('babelify', { presets: ['es2015', 'react', 'stage-1'] }), { verbose: true });
28 | bundle_js(bundler);
29 | bundler.on('update', () => {
30 | bundle_js(bundler);
31 | });
32 | bundler.on('log', (msg) => {
33 | console.log(msg);
34 | });
35 | });
36 |
37 | gulp.task('browserSync', function() {
38 | browserSync.init({
39 | notify: false,
40 | port: 3000,
41 | open: true,
42 | server: {
43 | baseDir: ['example/app']
44 | },
45 | });
46 | gulp.watch('example/app/bundle.js').on('change', browserSync.reload);
47 | });
48 |
49 | gulp.task('eslint', function() {
50 | return gulp.src(['src/**/*.js'])
51 | .pipe(eslint())
52 | .pipe(eslint.format())
53 | .pipe(eslint.failAfterError());
54 | });
55 |
56 | gulp.task('build:clean', function() {
57 | del.sync(['build']);
58 | });
59 |
60 | gulp.task('build', () => {
61 | return gulp.src('src/**/*.js')
62 | .pipe(babel({
63 | presets: ['es2015', 'stage-1', 'react'],
64 | plugins: ['transform-runtime']
65 | }))
66 | .pipe(gulp.dest('build/'));
67 | });
68 |
69 | gulp.task('build:copy', function() {
70 | return gulp.src(['package.json', 'README.md', 'LICENSE'])
71 | .pipe(copy('build/', {prefix: 1}));
72 | });
73 |
74 | gulp.task('test', function() {
75 | return gulp.src(['.mochasetup.js', 'test/**/*.spec.js'])
76 | .pipe(babel({
77 | presets: ['es2015', 'stage-1', 'react'],
78 | plugins: ['transform-runtime']
79 | }))
80 | .pipe(mocha({
81 | reporter: 'spec',
82 | }));
83 | });
84 |
85 | gulp.task('clean:build', ['build:clean', 'build', 'build:copy']);
86 |
87 | gulp.task('default', ['watchify', 'browserSync']);
88 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "material-ui-datatables",
3 | "version": "0.18.2",
4 | "description": "An another React Data tables component.",
5 | "main": "./src/index.js",
6 | "scripts": {
7 | "start": "gulp",
8 | "test": "gulp test",
9 | "test:coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
10 | "lint": "gulp eslint",
11 | "clean:build": "gulp clean:build"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/hyojin/material-ui-datatables.git"
16 | },
17 | "keywords": [
18 | "react",
19 | "react-component",
20 | "material design",
21 | "material-ui",
22 | "datatables"
23 | ],
24 | "author": "Hyojin Kwak",
25 | "license": "MIT",
26 | "peerDependencies": {
27 | "react": "^15.5.4",
28 | "react-dom": "^15.5.4",
29 | "react-tap-event-plugin": "^1.0.0 || ^2.0.0"
30 | },
31 | "dependencies": {
32 | "babel-runtime": "^6.18.0",
33 | "material-ui": "0.18.0",
34 | "prop-types": "^15.5.10"
35 | },
36 | "devDependencies": {
37 | "babel-core": "^6.18.2",
38 | "babel-eslint": "^7.1.0",
39 | "babel-loader": "^6.2.7",
40 | "babel-plugin-transform-runtime": "^6.15.0",
41 | "babel-preset-es2015": "^6.16.0",
42 | "babel-preset-react": "^6.16.0",
43 | "babel-preset-stage-1": "^6.16.0",
44 | "babelify": "^7.3.0",
45 | "browser-sync": "^2.17.5",
46 | "browserify": "^13.1.0",
47 | "chai": "^3.5.0",
48 | "coveralls": "^2.11.16",
49 | "del": "^2.2.2",
50 | "enzyme": "^2.8.2",
51 | "eslint": "^3.9.1",
52 | "eslint-plugin-babel": "^3.3.0",
53 | "eslint-plugin-material-ui": "^1.0.1",
54 | "eslint-plugin-react": "^6.6.0",
55 | "gulp": "^3.9.1",
56 | "gulp-babel": "^6.1.2",
57 | "gulp-copy": "0.0.2",
58 | "gulp-eslint": "^3.0.1",
59 | "gulp-load-plugins": "^1.3.0",
60 | "gulp-mocha": "^3.0.1",
61 | "jsdom": "^9.10.0",
62 | "mocha-lcov-reporter": "^1.2.0",
63 | "nyc": "^10.1.2",
64 | "react": "^15.5.4",
65 | "react-addons-test-utils": "^15.4.2",
66 | "react-dom": "^15.5.4",
67 | "react-tap-event-plugin": "^2.0.0",
68 | "sinon": "^1.17.7",
69 | "vinyl-source-stream": "^1.1.0",
70 | "watchify": "^3.7.0",
71 | "webpack": "^1.13.3"
72 | },
73 | "browserify": {
74 | "transform": [["babelify", {"presets": ["es2015", "react", "stage-1"]}]]
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/DataTables/DataTables.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import PropTypes from 'prop-types';
3 | import {TableHeader, TableRow} from 'material-ui/Table';
4 | import {Toolbar} from 'material-ui/Toolbar';
5 | import DropDownMenu from 'material-ui/DropDownMenu';
6 | import MenuItem from 'material-ui/MenuItem';
7 | import FlatButton from 'material-ui/FlatButton';
8 | import ChevronLeft from 'material-ui/svg-icons/navigation/chevron-left';
9 | import ChevronRight from 'material-ui/svg-icons/navigation/chevron-right';
10 | // customized components
11 | import DataTablesTable from './DataTablesTable';
12 | import DataTablesTableBody from './DataTablesTableBody';
13 | import DataTablesHeaderColumn from './DataTablesHeaderColumn';
14 | import DataTablesRow from './DataTablesRow';
15 | import DataTablesRowColumn from './DataTablesRowColumn';
16 | import DataTablesHeaderToolbar from './DataTablesHeaderToolbar';
17 |
18 | function getStyles(props, context) {
19 | const {
20 | baseTheme: {
21 | palette,
22 | },
23 | table,
24 | tableHeaderColumn,
25 | } = context.muiTheme;
26 |
27 | return {
28 | tableHeaderColumn: {
29 | fontWeight: 600,
30 | },
31 | footerToolbar: {
32 | backgroundColor: table.backgroundColor,
33 | borderTop: `1px solid ${palette.borderColor}`,
34 | },
35 | footerControlGroup: {
36 | fontSize: 12,
37 | color: tableHeaderColumn.textColor,
38 | marginLeft: 'auto',
39 | display: 'flex',
40 | },
41 | footerToolbarItem: {
42 | marginLeft: 8,
43 | marginRight: 8,
44 | alignItems: 'center',
45 | display: 'flex',
46 | },
47 | paginationButtons: {
48 | marginLeft: 24,
49 | },
50 | paginationButton: {
51 | minWidth: 36,
52 | opacity: 0.54,
53 | },
54 | rowSizeMenu: {
55 | color: tableHeaderColumn.textColor,
56 | },
57 | rowSizeControlsWrapper: {
58 | display: 'flex',
59 | },
60 | };
61 | }
62 |
63 | function isRowSelected(index, selectedRows) {
64 | if (Array.isArray(selectedRows)) {
65 | return selectedRows.includes(index);
66 | } else {
67 | return undefined;
68 | }
69 | }
70 |
71 | class DataTables extends Component {
72 | static muiName = 'DataTables';
73 |
74 | static propTypes = {
75 | columns: PropTypes.array.isRequired,
76 | count: PropTypes.number,
77 | data: PropTypes.array,
78 | deselectOnClickaway: PropTypes.bool,
79 | enableSelectAll: PropTypes.bool,
80 | filterHintText: PropTypes.string,
81 | filterValue: PropTypes.string,
82 | fixedFooter: PropTypes.bool,
83 | fixedHeader: PropTypes.bool,
84 | footerToolbarStyle: PropTypes.object,
85 | headerToolbarMode: PropTypes.string,
86 | height: PropTypes.string,
87 | initialSort: PropTypes.object,
88 | multiSelectable: PropTypes.bool,
89 | onCellClick: PropTypes.func,
90 | onCellDoubleClick: PropTypes.func,
91 | onFilterValueChange: PropTypes.func,
92 | onNextPageClick: PropTypes.func,
93 | onPreviousPageClick: PropTypes.func,
94 | onRowSelection: PropTypes.func,
95 | onRowSizeChange: PropTypes.func,
96 | onSortOrderChange: PropTypes.func,
97 | page: PropTypes.number,
98 | rowSize: PropTypes.number,
99 | rowSizeLabel: PropTypes.string,
100 | rowSizeList: PropTypes.array,
101 | selectable: PropTypes.bool,
102 | selectedRows: PropTypes.array,
103 | showCheckboxes: PropTypes.bool,
104 | showFooterToolbar: PropTypes.bool,
105 | showHeaderToolbar: PropTypes.bool,
106 | showHeaderToolbarFilterIcon: PropTypes.bool,
107 | showRowHover: PropTypes.bool,
108 | showRowSizeControls: PropTypes.bool,
109 | stripedRows: PropTypes.bool,
110 | summaryLabelTemplate: PropTypes.func,
111 | tableBodyStyle: PropTypes.object,
112 | tableHeaderColumnStyle: PropTypes.object,
113 | tableHeaderStyle: PropTypes.object,
114 | tableRowColumnStyle: PropTypes.object,
115 | tableRowStyle: PropTypes.object,
116 | tableStyle: PropTypes.object,
117 | tableWrapperStyle: PropTypes.object,
118 | title: PropTypes.string,
119 | titleStyle: PropTypes.object,
120 | toolbarIconRight: PropTypes.node,
121 | };
122 |
123 | static contextTypes = {
124 | muiTheme: PropTypes.object.isRequired,
125 | };
126 |
127 | static defaultProps = {
128 | rowSize: 10,
129 | rowSizeLabel: 'Rows per page:',
130 | rowSizeList: [10, 30, 50, 100],
131 | summaryLabelTemplate: (start, end, count) => {
132 | return `${start} - ${end} of ${count}`;
133 | },
134 | showRowSizeControls: true,
135 | filterHintText: 'Search',
136 | columns: [],
137 | data: [],
138 | page: 1,
139 | count: 0,
140 | fixedHeader: false,
141 | fixedFooter: false,
142 | stripedRows: false,
143 | showRowHover: false,
144 | selectable: false,
145 | selectedRows: undefined,
146 | multiSelectable: false,
147 | enableSelectAll: false,
148 | deselectOnClickaway: false,
149 | showCheckboxes: false,
150 | height: 'inherit',
151 | showHeaderToolbar: false,
152 | showFooterToolbar: true,
153 | initialSort: {
154 | column: '',
155 | order: 'asc',
156 | },
157 | };
158 |
159 | constructor(props, context) {
160 | super(props, context);
161 | this.state = {
162 | sort: props.initialSort,
163 | };
164 | }
165 |
166 | handleHeaderColumnClick = (event, rowIndex, columnIndex) => {
167 | const adjustedColumnIndex = columnIndex - 1;
168 | const column = this.props.columns[adjustedColumnIndex];
169 | if (column && column.sortable) {
170 | const {sort} = this.state;
171 | const {onSortOrderChange} = this.props;
172 | const key = column.key;
173 | const order = sort.column === column.key && sort.order === 'asc' ? 'desc' : 'asc';
174 | this.setState({
175 | sort: {
176 | column: key,
177 | order: order,
178 | },
179 | });
180 | if (onSortOrderChange) {
181 | onSortOrderChange(key, order);
182 | }
183 | }
184 | }
185 |
186 | handleCellClick = (rowIndex, columnIndex, event) => {
187 | const {onCellClick, selectable} = this.props;
188 | if (onCellClick && !selectable) {
189 | const adjustedColumnIndex = this.props.showCheckboxes ? columnIndex : columnIndex - 1;
190 | onCellClick(
191 | rowIndex,
192 | adjustedColumnIndex,
193 | // row data
194 | this.props.data[rowIndex],
195 | // clicked column
196 | this.props.data[rowIndex][this.props.columns[adjustedColumnIndex].key],
197 | event
198 | );
199 | }
200 | }
201 |
202 | handleCellDoubleClick = (rowIndex, columnIndex, event) => {
203 | const {onCellDoubleClick} = this.props;
204 | if (onCellDoubleClick) {
205 | const adjustedColumnIndex = this.props.showCheckboxes ? columnIndex : columnIndex - 1;
206 | onCellDoubleClick(
207 | rowIndex,
208 | adjustedColumnIndex,
209 | // row data
210 | this.props.data[rowIndex],
211 | // clicked column
212 | this.props.data[rowIndex][this.props.columns[adjustedColumnIndex].key],
213 | event
214 | );
215 | }
216 | }
217 |
218 | handleRowSizeChange = (event, index, value) => {
219 | const {onRowSizeChange} = this.props;
220 | if (onRowSizeChange) {
221 | onRowSizeChange(index, value);
222 | }
223 | }
224 |
225 | handlePreviousPageClick = (event) => {
226 | const {onPreviousPageClick} = this.props;
227 | if (onPreviousPageClick) {
228 | onPreviousPageClick(event);
229 | }
230 | }
231 |
232 | handleNextPageClick = (event) => {
233 | const {onNextPageClick} = this.props;
234 | if (onNextPageClick) {
235 | onNextPageClick(event);
236 | }
237 | }
238 |
239 | handleFilterValueChange = (value) => {
240 | const {onFilterValueChange} = this.props;
241 | if (onFilterValueChange) {
242 | onFilterValueChange(value);
243 | }
244 | }
245 |
246 | handleRowSelection = (selectedRows) => {
247 | const {onRowSelection} = this.props;
248 | if (onRowSelection) {
249 | onRowSelection(selectedRows);
250 | }
251 | }
252 |
253 | renderTableRowColumnData = (row, column) => {
254 | if (column.render) return column.render(row[column.key], row);
255 | return row[column.key];
256 | }
257 |
258 | render() {
259 | const {
260 | title,
261 | titleStyle,
262 | filterHintText,
263 | fixedHeader,
264 | fixedFooter,
265 | footerToolbarStyle,
266 | stripedRows,
267 | showRowHover,
268 | selectable,
269 | multiSelectable,
270 | enableSelectAll,
271 | deselectOnClickaway,
272 | showCheckboxes,
273 | height,
274 | showHeaderToolbar,
275 | showFooterToolbar,
276 | rowSize,
277 | rowSizeLabel,
278 | rowSizeList,
279 | showRowSizeControls,
280 | summaryLabelTemplate,
281 | columns,
282 | data,
283 | page,
284 | toolbarIconRight,
285 | count,
286 | tableStyle,
287 | tableBodyStyle,
288 | tableHeaderColumnStyle,
289 | tableHeaderStyle,
290 | tableRowColumnStyle,
291 | tableRowStyle,
292 | tableWrapperStyle,
293 | headerToolbarMode,
294 | filterValue,
295 | showHeaderToolbarFilterIcon,
296 | ...other, // eslint-disable-line no-unused-vars, comma-dangle
297 | } = this.props;
298 |
299 | const styles = getStyles(this.props, this.context);
300 |
301 | let start = (page - 1) * rowSize + 1;
302 | let end = (page - 1) * rowSize + rowSize;
303 | const totalCount = count === 0 ? data.length : count;
304 | let previousButtonDisabled = page === 1;
305 | let nextButtonDisabled = false;
306 | if (totalCount === 0) {
307 | start = 0;
308 | previousButtonDisabled = true;
309 | } else if (start > totalCount) {
310 | start = 1;
311 | previousButtonDisabled = true;
312 | }
313 | if (end >= totalCount) {
314 | end = totalCount;
315 | nextButtonDisabled = true;
316 | }
317 |
318 | let headerToolbar;
319 | if (showHeaderToolbar) {
320 | headerToolbar = (
321 |
331 | );
332 | }
333 |
334 | let rowSizeControls = null;
335 | if (showRowSizeControls) {
336 | rowSizeControls = (
337 |
338 |
339 |
{rowSizeLabel}
340 |
341 | {
342 | rowSizeList.length > 0 ?
343 | (
344 |
349 | {rowSizeList.map((rowSize) => {
350 | return (
351 |
356 | );
357 | })}
358 |
359 | ) :
360 | null
361 | }
362 |
363 | );
364 | }
365 |
366 | let footerToolbar;
367 | if (showFooterToolbar) {
368 | footerToolbar = (
369 |
370 |
371 | {rowSizeControls}
372 |
373 |
{summaryLabelTemplate(start, end, totalCount)}
374 |
375 |
376 | }
378 | style={styles.paginationButton}
379 | onClick={this.handlePreviousPageClick}
380 | disabled={previousButtonDisabled}
381 | />
382 | }
384 | style={styles.paginationButton}
385 | onClick={this.handleNextPageClick}
386 | disabled={nextButtonDisabled}
387 | />
388 |
389 |
390 |
391 | );
392 | }
393 |
394 | return (
395 |
396 | {headerToolbar}
397 |
410 |
416 |
420 | {columns.map((column, index) => {
421 | const style = Object.assign({}, styles.tableHeaderColumn, tableHeaderColumnStyle, column.style || {});
422 | const sortable = column.sortable;
423 | const sorted = this.state.sort.column === column.key;
424 | const order = sorted ? this.state.sort.order : 'asc';
425 | return (
426 |
436 | {column.label}
437 |
438 | );
439 | }, this)}
440 |
441 |
442 |
448 | {data.map((row, index) => {
449 | return (
450 |
455 | {columns.map((column, index) => {
456 | return (
457 |
462 | {this.renderTableRowColumnData(row, column)}
463 |
464 | );
465 | })}
466 |
467 | );
468 | })}
469 |
470 |
471 | {footerToolbar}
472 |
473 | );
474 | }
475 | }
476 |
477 | export default DataTables;
478 |
--------------------------------------------------------------------------------
/src/DataTables/DataTablesHeaderColumn.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import {TableHeaderColumn} from 'material-ui/Table';
4 | import Tooltip from 'material-ui/internal/Tooltip';
5 | import ArrowUpward from 'material-ui/svg-icons/navigation/arrow-upward';
6 | import ArrowDownward from 'material-ui/svg-icons/navigation/arrow-downward';
7 |
8 | function getStyles(props, context, state) {
9 | const {tableHeaderColumn} = context.muiTheme;
10 |
11 | return {
12 | root: {
13 | fontWeight: 'normal',
14 | fontSize: 12,
15 | paddingLeft: tableHeaderColumn.spacing,
16 | paddingRight: tableHeaderColumn.spacing,
17 | height: tableHeaderColumn.height,
18 | textAlign: props.alignRight ? 'right' : 'left',
19 | whiteSpace: 'nowrap',
20 | textOverflow: 'ellipsis',
21 | color: props.sorted ? '#3A3A3A' : tableHeaderColumn.textColor,
22 | position: 'relative',
23 | cursor: props.sortable ? 'pointer' : 'default',
24 | },
25 | tooltip: {
26 | boxSizing: 'border-box',
27 | marginTop: tableHeaderColumn.height / 2,
28 | },
29 | sortIcon: {
30 | height: '100%',
31 | width: '100%',
32 | opacity: props.sorted ? 0.87 : 0.54,
33 | display: state.sortHovered || props.sorted ? 'inline' : 'none',
34 | },
35 | iconWrapper: {
36 | display: 'inline-block',
37 | height: 16,
38 | width: 16,
39 | verticalAlign: 'middle',
40 | marginLeft: 8,
41 | marginRight: 8,
42 | },
43 | titleWrapper: {
44 | display: 'inline-block',
45 | verticalAlign: 'middle',
46 | },
47 | };
48 | }
49 |
50 | class DataTablesHeaderColumn extends TableHeaderColumn {
51 | static muiName = 'DataTablesHeaderColumn';
52 |
53 | static propTypes = {
54 | children: PropTypes.node,
55 | /**
56 | * The css class name of the root element.
57 | */
58 | className: PropTypes.string,
59 | /**
60 | * Number to identify the header row. This property
61 | * is automatically populated when used with TableHeader.
62 | */
63 | columnNumber: PropTypes.number,
64 | /**
65 | * @ignore
66 | * Not used here but we need to remove it from the root element.
67 | */
68 | hoverable: PropTypes.bool,
69 | /** @ignore */
70 | onClick: PropTypes.func,
71 | /**
72 | * @ignore
73 | * Not used here but we need to remove it from the root element.
74 | */
75 | onHover: PropTypes.func,
76 | /**
77 | * @ignore
78 | * Not used here but we need to remove it from the root element.
79 | */
80 | onHoverExit: PropTypes.func,
81 | /**
82 | * Override the inline-styles of the root element.
83 | */
84 | order: PropTypes.string,
85 | sortable: PropTypes.bool,
86 | style: PropTypes.object,
87 | /**
88 | * The string to supply to the tooltip. If not
89 | * string is supplied no tooltip will be shown.
90 | */
91 | tooltip: PropTypes.string,
92 | /**
93 | * Additional styling that can be applied to the tooltip.
94 | */
95 | tooltipStyle: PropTypes.object,
96 |
97 |
98 | };
99 |
100 | static defaultProps = {
101 | sortable: false,
102 | order: 'asc',
103 | };
104 |
105 | constructor(props, context) {
106 | super(props, context);
107 | this.state = {
108 | sortHovered: false,
109 | };
110 | }
111 |
112 | onMouseEnter = () => {
113 | if (this.props.tooltip !== undefined) {
114 | this.setState({hovered: true});
115 | }
116 | if (this.props.sortable) {
117 | this.setState({sortHovered: true});
118 | }
119 | };
120 |
121 | onMouseLeave = () => {
122 | if (this.props.tooltip !== undefined) {
123 | this.setState({hovered: false});
124 | }
125 | if (this.props.sortable) {
126 | this.setState({sortHovered: false});
127 | }
128 | };
129 |
130 | render() {
131 | const {
132 | children,
133 | className,
134 | columnNumber, // eslint-disable-line no-unused-vars
135 | hoverable, // eslint-disable-line no-unused-vars
136 | onClick, // eslint-disable-line no-unused-vars
137 | onHover, // eslint-disable-line no-unused-vars
138 | onHoverExit, // eslint-disable-line no-unused-vars
139 | style,
140 | tooltip,
141 | tooltipStyle,
142 | sortable,
143 | sorted,
144 | order,
145 | alignRight, // eslint-disable-line no-unused-vars
146 | ...other, // eslint-disable-line comma-dangle
147 | } = this.props;
148 |
149 | const {prepareStyles} = this.context.muiTheme;
150 | const styles = getStyles(this.props, this.context, this.state);
151 |
152 | const handlers = {
153 | onMouseEnter: this.onMouseEnter,
154 | onMouseLeave: this.onMouseLeave,
155 | onClick: this.onClick,
156 | };
157 |
158 | let tooltipNode;
159 |
160 | if (tooltip !== undefined) {
161 | tooltipNode = (
162 |
167 | );
168 | }
169 |
170 | let sortIcon;
171 |
172 | if (sorted && order === 'asc') {
173 | sortIcon = ();
174 | } else if (sorted && order === 'desc') {
175 | sortIcon = ();
176 | } else if (sortable) {
177 | sortIcon = ();
178 | }
179 |
180 | let leftSortIcon;
181 | let rightSortIcon;
182 |
183 | if (sortable && styles.root.textAlign === 'left') {
184 | rightSortIcon = sortIcon;
185 | } else if (sortable && styles.root.textAlign === 'right') {
186 | leftSortIcon = sortIcon;
187 | }
188 |
189 | const titleNode = ({children}
);
190 |
191 | return (
192 |
198 | {tooltipNode}
199 | {leftSortIcon}
200 | {titleNode}
201 | {rightSortIcon}
202 | |
203 | );
204 | }
205 | }
206 |
207 | export default DataTablesHeaderColumn;
208 |
--------------------------------------------------------------------------------
/src/DataTables/DataTablesHeaderToolbar.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import PropTypes from 'prop-types';
3 | import {Toolbar, ToolbarGroup, ToolbarTitle} from 'material-ui/Toolbar';
4 | import IconButton from 'material-ui/IconButton';
5 | import ClearIcon from 'material-ui/svg-icons/content/clear';
6 | import FilterListIcon from 'material-ui/svg-icons/content/filter-list';
7 | import SearchIcon from 'material-ui/svg-icons/action/search';
8 | import TextField from 'material-ui/TextField';
9 | import {blue500} from 'material-ui/styles/colors';
10 |
11 | function getStyles(context) {
12 | const {
13 | table,
14 | } = context.muiTheme;
15 |
16 | return {
17 | headerToolbar: {
18 | backgroundColor: table.backgroundColor,
19 | height: 64,
20 | paddingRight: 8,
21 | },
22 | icon: {
23 | opacity: 0.64,
24 | },
25 | headerToolbarSearchIcon: {
26 | marginTop: 12,
27 | },
28 | headerToolbarIconButton: {
29 | marginTop: 6,
30 | },
31 | searchToolbarGroup: {
32 | width: '100%',
33 | display: 'flex',
34 | alignItems: 'center',
35 | },
36 | searchInputTextField: {
37 | marginTop: 6,
38 | marginLeft: 8,
39 | width: '100%',
40 | minWidth: 60,
41 | },
42 | headerToolbarDefaultIcons: {
43 | display: 'flex',
44 | alignItems: 'center',
45 | },
46 | toolbarTitle: {
47 | lineHeight: '72px',
48 | },
49 | };
50 | }
51 |
52 | class DataTablesHeaderToolbar extends Component {
53 | static muiName = 'DataTablesHeaderToolbar';
54 |
55 | static propTypes = {
56 | filterHintText: PropTypes.string,
57 | filterValue: PropTypes.string,
58 | handleFilterValueChange: PropTypes.func,
59 | mode: PropTypes.string,
60 | onFilterValueChange: PropTypes.func,
61 | showFilterIcon: PropTypes.bool,
62 | title: PropTypes.string,
63 | titleStyle: PropTypes.object,
64 | toolbarIconRight: PropTypes.node,
65 | };
66 |
67 | static defaultProps = {
68 | mode: 'default',
69 | filterValue: '',
70 | showFilterIcon: true,
71 | };
72 |
73 | static contextTypes = {
74 | muiTheme: PropTypes.object.isRequired,
75 | };
76 |
77 | constructor(props, context) {
78 | super(props, context);
79 | this.filterValueTimer = undefined;
80 | this.filterInput = undefined;
81 | this.state = {
82 | mode: props.mode,
83 | filterValue: props.filterValue,
84 | };
85 | }
86 |
87 | componentDidUpdate(prevProps, prevState) {
88 | if (prevState.mode === 'default' && this.state.mode === 'filter') {
89 | if (this.filterInput) {
90 | this.filterInput.focus();
91 | }
92 | }
93 | }
94 |
95 | handleFilterClick = () => {
96 | const mode = this.state.mode === 'default' ? 'filter' : 'default';
97 | const {filterValue} = this.state;
98 | this.setState({
99 | mode: mode,
100 | filterValue: '',
101 | });
102 | if (mode === 'default' && filterValue !== '') {
103 | this.emitFilterValueChange('');
104 | }
105 | }
106 |
107 | handleClearClick = () => {
108 | const {filterValue} = this.state;
109 | if (filterValue !== '') {
110 | this.setState({
111 | filterValue: '',
112 | });
113 | this.emitFilterValueChange('');
114 | }
115 | }
116 |
117 | handleFilterValueChange = (event) => {
118 | const value = event.target.value;
119 | this.setState({
120 | filterValue: value,
121 | });
122 | clearTimeout(this.filterValueTimer);
123 | this.filterValueTimer = setTimeout(() => {
124 | this.emitFilterValueChange(value);
125 | }, 500);
126 | }
127 |
128 | emitFilterValueChange = (value) => {
129 | const {onFilterValueChange} = this.props;
130 | if (onFilterValueChange) {
131 | onFilterValueChange(value);
132 | }
133 | }
134 |
135 | render() {
136 | const {
137 | filterHintText,
138 | toolbarIconRight,
139 | title, // eslint-disable-line no-unused-vars
140 | titleStyle,
141 | showFilterIcon,
142 | ...other, // eslint-disable-line no-unused-vars, comma-dangle
143 | } = this.props;
144 |
145 | const {
146 | mode,
147 | filterValue,
148 | } = this.state;
149 |
150 | const styles = getStyles(this.context);
151 |
152 | let contentNode;
153 | let filterIconNode;
154 |
155 | if (mode === 'default') {
156 | contentNode = ();
157 | } else if (mode === 'filter') {
158 | contentNode = (
159 |
160 |
161 |
162 |
163 |
164 | {
171 | this.filterInput = textField ? textField.input : null;
172 | }}
173 | />
174 |
175 |
176 |
180 |
181 |
182 |
183 |
184 | );
185 | }
186 |
187 | const toolbarIconRightChildren = [];
188 | if (toolbarIconRight) {
189 | if (toolbarIconRight.length) {
190 | toolbarIconRight.map((toolbarIcon, i) => {
191 | toolbarIconRightChildren.push(React.cloneElement(
192 | toolbarIcon,
193 | {
194 | style: Object.assign(styles.headerToolbarIconButton, styles.icon),
195 | key: i,
196 | }
197 | ));
198 | });
199 | } else {
200 | toolbarIconRightChildren.push(React.cloneElement(
201 | toolbarIconRight,
202 | {
203 | style: Object.assign(styles.headerToolbarIconButton, styles.icon),
204 | key: 1,
205 | }
206 | ));
207 | }
208 | }
209 |
210 | if (showFilterIcon) {
211 | filterIconNode = (
212 |
216 |
219 |
220 | );
221 | }
222 |
223 | return (
224 |
225 | {contentNode}
226 |
227 | {filterIconNode}
228 | {toolbarIconRightChildren}
229 |
230 |
231 | );
232 | }
233 | }
234 |
235 | export default DataTablesHeaderToolbar;
236 |
--------------------------------------------------------------------------------
/src/DataTables/DataTablesRow.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import {TableRow} from 'material-ui/Table';
4 |
5 | function getStyles(props, context, state) {
6 | const {tableRow} = context.muiTheme;
7 |
8 | let cellBgColor = 'inherit';
9 | if (props.hovered || state.hovered) {
10 | cellBgColor = tableRow.hoverColor;
11 | } else if (props.selected) {
12 | cellBgColor = tableRow.selectedColor;
13 | } else if (props.striped) {
14 | cellBgColor = tableRow.stripeColor;
15 | }
16 |
17 | return {
18 | root: {
19 | borderBottom: props.displayBorder && `1px solid ${tableRow.borderColor}`,
20 | color: tableRow.textColor,
21 | height: tableRow.height,
22 | },
23 | cell: {
24 | backgroundColor: cellBgColor,
25 | },
26 | };
27 | }
28 |
29 | class DataTablesTableRow extends TableRow {
30 | static propTypes = {
31 | /**
32 | * Children passed to table row.
33 | */
34 | children: PropTypes.node,
35 | /**
36 | * The css class name of the root element.
37 | */
38 | className: PropTypes.string,
39 | /**
40 | * If true, row border will be displayed for the row.
41 | * If false, no border will be drawn.
42 | */
43 | displayBorder: PropTypes.bool,
44 | /**
45 | * Controls whether or not the row reponseds to hover events.
46 | */
47 | hoverable: PropTypes.bool,
48 | /**
49 | * Controls whether or not the row should be rendered as being
50 | * hovered. This property is evaluated in addition to this.state.hovered
51 | * and can be used to synchronize the hovered state with some other
52 | * external events.
53 | */
54 | hovered: PropTypes.bool,
55 | /**
56 | * @ignore
57 | * Called when a row cell is clicked.
58 | * rowNumber is the row number and columnId is
59 | * the column number or the column key.
60 | */
61 | onCellClick: PropTypes.func,
62 | /**
63 | * @ignore
64 | * Customized handler
65 | * Called when a row cell is double clicked.
66 | */
67 | onCellDoubleClick: PropTypes.func,
68 | /**
69 | * @ignore
70 | * Called when a table cell is hovered.
71 | * rowNumber is the row number of the hovered row
72 | * and columnId is the column number or the column key of the cell.
73 | */
74 | onCellHover: PropTypes.func,
75 | /**
76 | * @ignore
77 | * Called when a table cell is no longer hovered.
78 | * rowNumber is the row number of the row and columnId
79 | * is the column number or the column key of the cell.
80 | */
81 | onCellHoverExit: PropTypes.func,
82 | /**
83 | * @ignore
84 | * Called when row is clicked.
85 | */
86 | onRowClick: PropTypes.func,
87 | /**
88 | * @ignore
89 | * Called when a table row is hovered.
90 | * rowNumber is the row number of the hovered row.
91 | */
92 | onRowHover: PropTypes.func,
93 | /**
94 | * @ignore
95 | * Called when a table row is no longer hovered.
96 | * rowNumber is the row number of the row that is no longer hovered.
97 | */
98 | onRowHoverExit: PropTypes.func,
99 | /**
100 | * Number to identify the row. This property is
101 | * automatically populated when used with the TableBody component.
102 | */
103 | rowNumber: PropTypes.number,
104 | /**
105 | * If true, table rows can be selected. If multiple row
106 | * selection is desired, enable multiSelectable.
107 | * The default value is true.
108 | */
109 | selectable: PropTypes.bool,
110 | /**
111 | * Indicates that a particular row is selected.
112 | * This property can be used to programmatically select rows.
113 | */
114 | selected: PropTypes.bool,
115 | /**
116 | * Indicates whether or not the row is striped.
117 | */
118 | striped: PropTypes.bool,
119 | /**
120 | * Override the inline-styles of the root element.
121 | */
122 | style: PropTypes.object,
123 | };
124 |
125 | clicked = false;
126 |
127 | clickTimer = undefined;
128 |
129 | onCellClick = (event, columnIndex) => {
130 | event.persist();
131 | if (this.clicked) {
132 | this.clicked = false;
133 | clearTimeout(this.clickTimer);
134 | if (this.props.onCellDoubleClick) {
135 | this.props.onCellDoubleClick(event, this.props.rowNumber, columnIndex);
136 | }
137 | } else {
138 | this.clicked = true;
139 | this.clickTimer = setTimeout(() => {
140 | this.clicked = false;
141 | if (this.props.selectable && this.props.onCellClick) {
142 | this.props.onCellClick(event, this.props.rowNumber, columnIndex);
143 | }
144 | try {
145 | event.ctrlKey = true;
146 | } catch(e) {
147 | }
148 | this.onRowClick(event);
149 | }, 300);
150 | }
151 | };
152 |
153 | render() {
154 | const {
155 | className,
156 | displayBorder, // eslint-disable-line no-unused-vars
157 | hoverable, // eslint-disable-line no-unused-vars
158 | hovered, // eslint-disable-line no-unused-vars
159 | onCellClick, // eslint-disable-line no-unused-vars
160 | onCellDoubleClick, // eslint-disable-line no-unused-vars
161 | onCellHover, // eslint-disable-line no-unused-vars
162 | onCellHoverExit, // eslint-disable-line no-unused-vars
163 | onRowClick, // eslint-disable-line no-unused-vars
164 | onRowHover, // eslint-disable-line no-unused-vars
165 | onRowHoverExit, // eslint-disable-line no-unused-vars
166 | rowNumber, // eslint-disable-line no-unused-vars
167 | selectable, // eslint-disable-line no-unused-vars
168 | selected, // eslint-disable-line no-unused-vars
169 | striped, // eslint-disable-line no-unused-vars
170 | style,
171 | ...other, // eslint-disable-line comma-dangle
172 | } = this.props;
173 |
174 | const {prepareStyles} = this.context.muiTheme;
175 | const styles = getStyles(this.props, this.context, this.state);
176 |
177 | const rowColumns = React.Children.map(this.props.children, (child, columnNumber) => {
178 | if (React.isValidElement(child)) {
179 | return React.cloneElement(child, {
180 | columnNumber: columnNumber,
181 | hoverable: this.props.hoverable,
182 | key: `${this.props.rowNumber}-${columnNumber}`,
183 | onClick: this.onCellClick,
184 | onHover: this.onCellHover,
185 | onHoverExit: this.onCellHoverExit,
186 | style: Object.assign({}, styles.cell, child.props.style),
187 | });
188 | }
189 | });
190 |
191 | return (
192 |
197 | {rowColumns}
198 |
199 | );
200 | }
201 | }
202 |
203 | export default DataTablesTableRow;
204 |
--------------------------------------------------------------------------------
/src/DataTables/DataTablesRowColumn.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import {TableRowColumn} from 'material-ui/Table';
4 |
5 | function getStyles(props, context) {
6 | const {tableRowColumn} = context.muiTheme;
7 |
8 | const styles = {
9 | root: {
10 | paddingLeft: tableRowColumn.spacing,
11 | paddingRight: tableRowColumn.spacing,
12 | height: tableRowColumn.height,
13 | textAlign: props.alignRight ? 'right' : 'left',
14 | fontSize: 13,
15 | whiteSpace: 'nowrap',
16 | textOverflow: 'ellipsis',
17 | },
18 | };
19 |
20 | if (React.Children.count(props.children) === 1 && !isNaN(props.children)) {
21 | styles.textAlign = 'right';
22 | }
23 |
24 | return styles;
25 | }
26 |
27 | class DataTablesRowColumn extends TableRowColumn {
28 | static muiName = 'TableRowColumn';
29 |
30 | static propTypes = {
31 | children: PropTypes.node,
32 | /**
33 | * The css class name of the root element.
34 | */
35 | className: PropTypes.string,
36 | /**
37 | * @ignore
38 | * Number to identify the header row. This property
39 | * is automatically populated when used with TableHeader.
40 | */
41 | columnNumber: PropTypes.number,
42 | /**
43 | * @ignore
44 | * If true, this column responds to hover events.
45 | */
46 | hoverable: PropTypes.bool,
47 | /** @ignore */
48 | onClick: PropTypes.func,
49 | /** @ignore */
50 | onDoubleClick: PropTypes.func,
51 | /** @ignore */
52 | onHover: PropTypes.func,
53 | /**
54 | * @ignore
55 | * Callback function for hover exit event.
56 | */
57 | onHoverExit: PropTypes.func,
58 | /**
59 | * Override the inline-styles of the root element.
60 | */
61 | style: PropTypes.object,
62 | };
63 |
64 | onDoubleClick = (event) => {
65 | if (this.props.onDoubleClick) {
66 | this.props.onDoubleClick(event, this.props.columnNumber);
67 | }
68 | };
69 |
70 | render() {
71 | const {
72 | children,
73 | className,
74 | columnNumber, // eslint-disable-line no-unused-vars
75 | hoverable, // eslint-disable-line no-unused-vars
76 | onClick, // eslint-disable-line no-unused-vars
77 | onDoubleClick, // eslint-disable-line no-unused-vars
78 | onHover, // eslint-disable-line no-unused-vars
79 | onHoverExit, // eslint-disable-line no-unused-vars
80 | style,
81 | alignRight, // eslint-disable-line no-unused-vars
82 | ...other, // eslint-disable-line comma-dangle
83 | } = this.props;
84 |
85 | const {prepareStyles} = this.context.muiTheme;
86 | const styles = getStyles(this.props, this.context);
87 |
88 | const handlers = {
89 | onClick: this.onClick,
90 | onMouseEnter: this.onMouseEnter,
91 | onMouseLeave: this.onMouseLeave,
92 | onDoubleClick: this.onDoubleClick,
93 | };
94 |
95 | return (
96 |
102 | {children}
103 | |
104 | );
105 | }
106 | }
107 |
108 | export default DataTablesRowColumn;
109 |
--------------------------------------------------------------------------------
/src/DataTables/DataTablesTable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Table} from 'material-ui/Table';
3 |
4 | class DataTablesTable extends Table {
5 |
6 | createTableBody(base) {
7 | return React.cloneElement(
8 | base,
9 | {
10 | allRowsSelected: this.state.allRowsSelected,
11 | multiSelectable: this.props.multiSelectable,
12 | onCellClick: this.onCellClick,
13 | onCellDoubleClick: this.onCellDoubleClick,
14 | onCellHover: this.onCellHover,
15 | onCellHoverExit: this.onCellHoverExit,
16 | onRowHover: this.onRowHover,
17 | onRowHoverExit: this.onRowHoverExit,
18 | onRowSelection: this.onRowSelection,
19 | selectable: this.props.selectable,
20 | style: Object.assign({height: this.props.height}, base.props.style),
21 | }
22 | );
23 | }
24 |
25 | onCellDoubleClick = (rowNumber, columnNumber, event) => {
26 | if (this.props.onCellDoubleClick) this.props.onCellDoubleClick(rowNumber, columnNumber, event);
27 | };
28 | }
29 |
30 | export default DataTablesTable;
31 |
--------------------------------------------------------------------------------
/src/DataTables/DataTablesTableBody.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import {TableBody} from 'material-ui/Table';
4 | import ClickAwayListener from 'material-ui/internal/ClickAwayListener';
5 |
6 | class DataTablesTableBody extends TableBody {
7 | static muiName = 'TableBody';
8 |
9 | static propTypes = {
10 | /**
11 | * @ignore
12 | * Set to true to indicate that all rows should be selected.
13 | */
14 | allRowsSelected: PropTypes.bool,
15 | /**
16 | * Children passed to table body.
17 | */
18 | children: PropTypes.node,
19 | /**
20 | * The css class name of the root element.
21 | */
22 | className: PropTypes.string,
23 | /**
24 | * Controls whether or not to deselect all selected
25 | * rows after clicking outside the table.
26 | */
27 | deselectOnClickaway: PropTypes.bool,
28 | /**
29 | * Controls the display of the row checkbox. The default value is true.
30 | */
31 | displayRowCheckbox: PropTypes.bool,
32 | /**
33 | * @ignore
34 | * If true, multiple table rows can be selected.
35 | * CTRL/CMD+Click and SHIFT+Click are valid actions.
36 | * The default value is false.
37 | */
38 | multiSelectable: PropTypes.bool,
39 | /**
40 | * @ignore
41 | * Callback function for when a cell is clicked.
42 | */
43 | onCellClick: PropTypes.func,
44 | /**
45 | * @ignore
46 | * Customized handler
47 | * Callback function for when a cell is double clicked.
48 | */
49 | onCellDoubleClick: PropTypes.func,
50 | /**
51 | * @ignore
52 | * Called when a table cell is hovered. rowNumber
53 | * is the row number of the hovered row and columnId
54 | * is the column number or the column key of the cell.
55 | */
56 | onCellHover: PropTypes.func,
57 | /**
58 | * @ignore
59 | * Called when a table cell is no longer hovered.
60 | * rowNumber is the row number of the row and columnId
61 | * is the column number or the column key of the cell.
62 | */
63 | onCellHoverExit: PropTypes.func,
64 | /**
65 | * @ignore
66 | * Called when a table row is hovered.
67 | * rowNumber is the row number of the hovered row.
68 | */
69 | onRowHover: PropTypes.func,
70 | /**
71 | * @ignore
72 | * Called when a table row is no longer
73 | * hovered. rowNumber is the row number of the row
74 | * that is no longer hovered.
75 | */
76 | onRowHoverExit: PropTypes.func,
77 | /**
78 | * @ignore
79 | * Called when a row is selected. selectedRows is an
80 | * array of all row selections. IF all rows have been selected,
81 | * the string "all" will be returned instead to indicate that
82 | * all rows have been selected.
83 | */
84 | onRowSelection: PropTypes.func,
85 | /**
86 | * Controls whether or not the rows are pre-scanned to determine
87 | * initial state. If your table has a large number of rows and
88 | * you are experiencing a delay in rendering, turn off this property.
89 | */
90 | preScanRows: PropTypes.bool,
91 | /**
92 | * @ignore
93 | * If true, table rows can be selected. If multiple
94 | * row selection is desired, enable multiSelectable.
95 | * The default value is true.
96 | */
97 | selectable: PropTypes.bool,
98 | /**
99 | * If true, table rows will be highlighted when
100 | * the cursor is hovering over the row. The default
101 | * value is false.
102 | */
103 | showRowHover: PropTypes.bool,
104 | /**
105 | * If true, every other table row starting
106 | * with the first row will be striped. The default value is false.
107 | */
108 | stripedRows: PropTypes.bool,
109 | /**
110 | * Override the inline-styles of the root element.
111 | */
112 | style: PropTypes.object,
113 | };
114 |
115 | createRows() {
116 | const numChildren = React.Children.count(this.props.children);
117 | let rowNumber = 0;
118 | const handlers = {
119 | onCellClick: this.onCellClick,
120 | onCellDoubleClick: this.onCellDoubleClick,
121 | onCellHover: this.onCellHover,
122 | onCellHoverExit: this.onCellHoverExit,
123 | onRowHover: this.onRowHover,
124 | onRowHoverExit: this.onRowHoverExit,
125 | onRowClick: this.onRowClick,
126 | };
127 |
128 | return React.Children.map(this.props.children, (child) => {
129 | if (React.isValidElement(child)) {
130 | const props = {
131 | hoverable: this.props.showRowHover,
132 | selected: this.isRowSelected(rowNumber),
133 | striped: this.props.stripedRows && (rowNumber % 2 === 0),
134 | rowNumber: rowNumber++,
135 | };
136 |
137 | if (rowNumber === numChildren) {
138 | props.displayBorder = false;
139 | }
140 |
141 | const children = [
142 | this.createRowCheckboxColumn(props),
143 | ];
144 |
145 | React.Children.forEach(child.props.children, (child) => {
146 | children.push(child);
147 | });
148 |
149 | return React.cloneElement(child, {...props, ...handlers}, children);
150 | }
151 | });
152 | }
153 |
154 | onCellDoubleClick = (event, rowNumber, columnNumber) => {
155 | event.stopPropagation();
156 | if (this.props.onCellDoubleClick) {
157 | this.props.onCellDoubleClick(rowNumber, this.getColumnId(columnNumber), event);
158 | }
159 | };
160 |
161 | render() {
162 | const {
163 | style,
164 | allRowsSelected, // eslint-disable-line no-unused-vars
165 | multiSelectable, // eslint-disable-line no-unused-vars
166 | onCellClick, // eslint-disable-line no-unused-vars
167 | onCellDoubleClick, // eslint-disable-line no-unused-vars
168 | onCellHover, // eslint-disable-line no-unused-vars
169 | onCellHoverExit, // eslint-disable-line no-unused-vars
170 | onRowHover, // eslint-disable-line no-unused-vars
171 | onRowHoverExit, // eslint-disable-line no-unused-vars
172 | onRowSelection, // eslint-disable-line no-unused-vars
173 | selectable, // eslint-disable-line no-unused-vars
174 | deselectOnClickaway, // eslint-disable-line no-unused-vars
175 | showRowHover, // eslint-disable-line no-unused-vars
176 | stripedRows, // eslint-disable-line no-unused-vars
177 | displayRowCheckbox, // eslint-disable-line no-unused-vars
178 | preScanRows, // eslint-disable-line no-unused-vars
179 | ...other
180 | } = this.props;
181 |
182 | const {prepareStyles} = this.context.muiTheme;
183 |
184 | return (
185 |
186 |
187 | {this.createRows()}
188 |
189 |
190 | );
191 | }
192 | }
193 |
194 | export default DataTablesTableBody;
195 |
--------------------------------------------------------------------------------
/src/DataTables/index.js:
--------------------------------------------------------------------------------
1 | export DataTables from './DataTables';
2 |
3 | export default from './DataTables';
4 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export DataTables from './DataTables';
2 |
3 | export default from './DataTables';
4 |
--------------------------------------------------------------------------------
/test/DataTables/DataTables.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import {shallow, mount} from 'enzyme';
4 | import {expect} from 'chai';
5 | import sinon from 'sinon';
6 |
7 | import getMuiTheme from 'material-ui/styles/getMuiTheme';
8 | import {TableHeader} from 'material-ui/Table';
9 | import {Toolbar, ToolbarTitle} from 'material-ui/Toolbar';
10 | import DropDownMenu from 'material-ui/DropDownMenu';
11 | import MenuItem from 'material-ui/MenuItem';
12 | import FlatButton from 'material-ui/FlatButton';
13 | import IconButton from 'material-ui/IconButton';
14 | import TextField from 'material-ui/TextField';
15 | import PersonAdd from 'material-ui/svg-icons/social/person-add';
16 | import InfoOutline from 'material-ui/svg-icons/action/info-outline';
17 | import FilterListIcon from 'material-ui/svg-icons/content/filter-list';
18 | import {deepOrange500} from 'material-ui/styles/colors';
19 |
20 | import {
21 | TABLE_COLUMNS,
22 | TABLE_COLUMNS_TOOLTIP,
23 | TABLE_COLUMNS_SORT_STYLE,
24 | TABLE_COLUMNS_CLASSNAME,
25 | TABLE_DATA,
26 | styles,
27 | } from './tableSettings';
28 | import DataTables from '../../src/DataTables/DataTables';
29 | import DataTablesHeaderColumn from '../../src/DataTables/DataTablesHeaderColumn';
30 | import DataTablesRow from '../../src/DataTables/DataTablesRow';
31 | import DataTablesRowColumn from '../../src/DataTables/DataTablesRowColumn';
32 | import DataTablesHeaderToolbar from '../../src/DataTables/DataTablesHeaderToolbar';
33 | import DataTablesTable from '../../src/DataTables/DataTablesTable';
34 |
35 | describe('', function() {
36 | describe('Basic', function() {
37 | let wrapper;
38 | const muiTheme = getMuiTheme();
39 |
40 | before(function() {
41 | // shallow rendering
42 | wrapper = shallow(
43 | ,
52 | {
53 | context: {muiTheme: muiTheme},
54 | }
55 | );
56 | });
57 |
58 | it('should render table header', function() {
59 | expect(wrapper.find(TableHeader)).to.have.length(1);
60 | });
61 | it('should render header columns', function() {
62 | const headerColumns = wrapper.find(DataTablesHeaderColumn);
63 | expect(headerColumns).to.have.length(8);
64 | expect(headerColumns.getNodes()[0].props.children.props.children).to.equal('Dessert (100g serving)');
65 | expect(headerColumns.getNodes()[1].props.children.props.children).to.equal('Calories');
66 | expect(headerColumns.getNodes()[2].props.children.props.children).to.equal('Fat (g)');
67 | expect(headerColumns.getNodes()[3].props.children.props.children).to.equal('Carbs (g)');
68 | expect(headerColumns.getNodes()[4].props.children.props.children).to.equal('Protein (g)');
69 | expect(headerColumns.getNodes()[5].props.children.props.children).to.equal('Sodium (mg)');
70 | expect(headerColumns.getNodes()[6].props.children.props.children).to.equal('Calcium (%)');
71 | expect(headerColumns.getNodes()[7].props.children.props.children).to.equal('Iron (%)');
72 | });
73 | it('should render 10 data rows by default', function() {
74 | const dataRows = wrapper.find(DataTablesRow);
75 | expect(dataRows).to.have.length(10);
76 | });
77 | it('should render data columns', function() {
78 | const dataRows = wrapper.find(DataTablesRow);
79 | expect(dataRows.getNodes()[3].props.children[0].props.children).to.equal('Cupcake');
80 | expect(dataRows.getNodes()[3].props.children[1].props.children).to.equal('159');
81 | expect(dataRows.getNodes()[3].props.children[2].props.children).to.equal('6.0');
82 | expect(dataRows.getNodes()[3].props.children[3].props.children).to.equal('24');
83 | expect(dataRows.getNodes()[3].props.children[4].props.children).to.equal('4.0');
84 | expect(dataRows.getNodes()[3].props.children[5].props.children).to.equal('87');
85 | expect(dataRows.getNodes()[3].props.children[6].props.children).to.equal('14%');
86 | expect(dataRows.getNodes()[3].props.children[7].props.children).to.equal('1%');
87 | });
88 | it('should render data columns render function', function() {
89 | const dataRows = wrapper.find(DataTablesRow);
90 | expect(dataRows.getNodes()[2].props.children[2].props.children).to.equal('6.0');
91 | });
92 | it('should render row size label', function() {
93 | const rowSizeLabelWrapper = wrapper.find({style: styles.footerToolbarItem}).getNodes()[0];
94 | expect(rowSizeLabelWrapper.props.children.props.children).to.equal('Rows per page:');
95 | });
96 | it('should render row size menu', function() {
97 | expect(wrapper.find(DropDownMenu)).to.have.length(1);
98 | });
99 | it('should render 10, 30, 50, 100 row size items by default', function() {
100 | const rowSizeItems = wrapper.find(MenuItem);
101 | expect(rowSizeItems).to.have.length(4);
102 | expect(rowSizeItems.getNodes()[0].props.value).to.equal(10);
103 | expect(rowSizeItems.getNodes()[1].props.value).to.equal(30);
104 | expect(rowSizeItems.getNodes()[2].props.value).to.equal(50);
105 | expect(rowSizeItems.getNodes()[3].props.value).to.equal(100);
106 | });
107 | it('should render summary label', function() {
108 | const summaryLabelWrapper = wrapper.find({style: styles.footerToolbarItem}).getNodes()[1];
109 | expect(summaryLabelWrapper.props.children.props.children).to.equal('1 - 10 of 100');
110 | });
111 | it('should render pagination buttons', function() {
112 | expect(wrapper.find(FlatButton)).to.have.length(2);
113 | });
114 | it('should not render checkboxes', function() {
115 | expect(wrapper.find('[type="checkbox"]')).to.have.length(0);
116 | });
117 | it('should render footer toolbar', function() {
118 | expect(wrapper.find({style: styles.footerToolbar})).to.have.length(1);
119 | });
120 | });
121 |
122 | describe('Selectable & Tooltip & Pagination', function() {
123 | const handleNextPageClick = sinon.spy();
124 | const handlePreviousPageClick = sinon.spy();
125 | const handleRowSelection = sinon.spy();
126 | let wrapper;
127 | const muiTheme = getMuiTheme();
128 |
129 | before(function() {
130 | // full rendering
131 | wrapper = mount(
132 | ,
147 | {
148 | context: {muiTheme: muiTheme},
149 | childContextTypes: {muiTheme: PropTypes.object},
150 | }
151 | );
152 |
153 | // dummy
154 | global.window.getSelection = function() {
155 | return {
156 | removeAllRanges: function() {
157 | },
158 | };
159 | };
160 | });
161 |
162 | it('should call row select handler', function(done) {
163 | wrapper.find(DataTablesRowColumn).first().simulate('click');
164 | setTimeout(() => {
165 | expect(handleRowSelection).to.have.property('callCount', 1);
166 | expect(wrapper.find(DataTablesRow).first().prop('selected')).to.equal(true);
167 | done();
168 | }, 500);
169 | });
170 | it('should render tooltips', function() {
171 | const headerColumns = wrapper.find(DataTablesHeaderColumn);
172 | expect(headerColumns.getNodes()[0].props.tooltip).to.equal('Dessert (100g serving)');
173 | expect(headerColumns.getNodes()[1].props.tooltip).to.equal('Calories');
174 | });
175 | it('should call pagination handler', function() {
176 | wrapper.find(FlatButton).last().simulate('click');
177 | expect(handleNextPageClick).to.have.property('callCount', 1);
178 | wrapper.setProps({page: 2});
179 | wrapper.find(FlatButton).first().simulate('click');
180 | expect(handlePreviousPageClick).to.have.property('callCount', 1);
181 | });
182 | it('should render checkboxes', function() {
183 | expect(wrapper.find('[type="checkbox"]')).to.have.length(11);
184 | });
185 | });
186 |
187 | describe('Filter & Column Sort & Styled Column', function() {
188 | const handleCellClick = sinon.spy();
189 | const handleCellDoubleClick = sinon.spy();
190 | const handleFilterValueChange = sinon.spy();
191 | const handleSortOrderChange = sinon.spy();
192 | const handleRowSizeChange = sinon.spy();
193 | let wrapper;
194 | const muiTheme = getMuiTheme();
195 |
196 | before(function() {
197 | // full rendering
198 | wrapper = mount(
199 | ,
215 | {
216 | context: {muiTheme: muiTheme},
217 | childContextTypes: {muiTheme: PropTypes.object},
218 | }
219 | );
220 |
221 | // dummy
222 | global.window.getSelection = function() {
223 | return {
224 | removeAllRanges: function() {
225 | },
226 | };
227 | };
228 | });
229 |
230 | it('should render header tool bar', function() {
231 | expect(wrapper.find(DataTablesHeaderToolbar)).to.have.length(1);
232 | });
233 | it('should render data tables title', function() {
234 | expect(wrapper.find(DataTablesHeaderToolbar).prop('title')).to.equal('Nutrition');
235 | });
236 | it('should call click handler', function(done) {
237 | wrapper.find(DataTablesRowColumn).first().simulate('click');
238 | setTimeout(() => {
239 | expect(handleCellClick).to.have.property('callCount', 1);
240 | done();
241 | }, 500);
242 | });
243 | it('should call dobule click handler', function() {
244 | // simulate double click
245 | wrapper.find(DataTablesRowColumn).first().simulate('click');
246 | wrapper.find(DataTablesRowColumn).first().simulate('click');
247 | expect(handleCellDoubleClick).to.have.property('callCount', 1);
248 | });
249 | it('should call filter value change handler and set focus to input', function(done) {
250 | wrapper.find(DataTablesHeaderToolbar).find(IconButton).simulate('click');
251 | const inputNodeInstanceId = Object.keys(wrapper.find(DataTablesHeaderToolbar).find(TextField).find('input').getDOMNode())[0];
252 | const inputDomId = wrapper.find(DataTablesHeaderToolbar).find(TextField).find('input').getDOMNode()[inputNodeInstanceId]._domID;
253 | const activeElementDomId = document.activeElement[inputNodeInstanceId]._domID;
254 | expect(inputDomId).to.equal(activeElementDomId);
255 | wrapper.find(DataTablesHeaderToolbar).find(TextField).find('input')
256 | .simulate('change', {target: {value: 'dummy'}});
257 | setTimeout(() => {
258 | expect(handleFilterValueChange).to.have.property('callCount', 1);
259 | expect(handleFilterValueChange.calledWith('dummy')).to.equal(true);
260 | done();
261 | }, 800);
262 | });
263 | it('should call row size change handler', function() {
264 | wrapper.find(DropDownMenu).props().onChange({}, 1, 30);
265 | expect(handleRowSizeChange).to.have.property('callCount', 1);
266 | expect(handleRowSizeChange.calledWith(1, 30)).to.equal(true);
267 | });
268 | it('should call sort order change handler', function() {
269 | wrapper.find(DataTablesHeaderColumn).first().simulate('click');
270 | expect(handleSortOrderChange).to.have.property('callCount', 1);
271 | expect(handleSortOrderChange.calledWith('name', 'asc')).to.equal(true);
272 | wrapper.find(DataTablesHeaderColumn).first().simulate('click');
273 | expect(handleSortOrderChange.calledWith('name', 'desc')).to.equal(true);
274 | });
275 | });
276 |
277 | describe('Internationalization', function() {
278 | let wrapper;
279 | const muiTheme = getMuiTheme();
280 |
281 | before(function() {
282 | // full rendering
283 | wrapper = mount(
284 | `${start} - ${end} ${count}件`
295 | }
296 | showCheckboxes={false}
297 | showHeaderToolbar={true}
298 | count={100}
299 | />,
300 | {
301 | context: {muiTheme: muiTheme},
302 | childContextTypes: {muiTheme: PropTypes.object},
303 | }
304 | );
305 | });
306 |
307 | it('should render data tables title', function() {
308 | expect(wrapper.find(DataTablesHeaderToolbar).prop('title')).to.equal('ニュートリション');
309 | });
310 | it('should render row size label with customized label', function() {
311 | expect(
312 | wrapper.find({style: styles.footerToolbarItem})
313 | .at(0).props().children.props.children
314 | )
315 | .to.equal('ページサイズ');
316 | });
317 | it('should render summary label with customized label', function() {
318 | expect(
319 | wrapper.find({style: styles.footerToolbarItem})
320 | .at(1).props().children.props.children
321 | )
322 | .to.equal('1 - 10 100件');
323 | });
324 | it('should render search hint with customized text', function() {
325 | wrapper.find(DataTablesHeaderToolbar).find(IconButton).simulate('click');
326 | expect(wrapper.find(DataTablesHeaderToolbar).find(TextField).prop('hintText')).to.equal('検索');
327 | });
328 | });
329 |
330 | describe('Toolbar Icons & Styled title & Styled table', function() {
331 | let wrapper;
332 | const muiTheme = getMuiTheme();
333 |
334 | before(function() {
335 | // full rendering
336 | wrapper = mount(
337 |
354 |
355 | ,
356 |
357 |
358 | ,
359 | ]}
360 | />,
361 | {
362 | context: {muiTheme: muiTheme},
363 | childContextTypes: {muiTheme: PropTypes.object},
364 | }
365 | );
366 | });
367 |
368 | it('should render custom tool bar icons', function() {
369 | expect(wrapper.find(DataTablesHeaderToolbar).find(IconButton)).to.have.length(3);
370 | expect(wrapper.find(DataTablesHeaderToolbar).find(FilterListIcon)).to.have.length(1);
371 | expect(wrapper.find(DataTablesHeaderToolbar).find(PersonAdd)).to.have.length(1);
372 | expect(wrapper.find(DataTablesHeaderToolbar).find(InfoOutline)).to.have.length(1);
373 | });
374 |
375 | it('should have inline styles for title', function() {
376 | expect(wrapper.find(ToolbarTitle).prop('style').fontSize).not.to.equal(undefined);
377 | expect(wrapper.find(ToolbarTitle).prop('style').color).not.to.equal(undefined);
378 | expect(wrapper.find(ToolbarTitle).prop('style').fontSize).to.equal(16);
379 | expect(wrapper.find(ToolbarTitle).prop('style').color).to.equal(deepOrange500);
380 | });
381 |
382 | it('should have footerToolbar styles defined', function() {
383 | expect(wrapper.find(Toolbar).at(1).prop('style').padding).to.equal('0 100px');
384 | });
385 |
386 | it('should have table styles defined', function() {
387 | expect(wrapper.find(DataTablesTable).at(0).prop('style').tableLayout).to.equal('auto');
388 | });
389 |
390 | it('should have table body styles defined', function() {
391 | expect(wrapper.find(DataTablesTable).at(0).prop('bodyStyle').overflowX).to.equal('auto');
392 | });
393 |
394 | it('should have table wrapper styles defined', function() {
395 | expect(wrapper.find(DataTablesTable).at(0).prop('wrapperStyle').padding).to.equal(5);
396 | });
397 | });
398 |
399 | describe('Column class name', function() {
400 | let wrapper;
401 | const muiTheme = getMuiTheme();
402 |
403 | before(function() {
404 | // full rendering
405 | wrapper = mount(
406 | ,
418 | {
419 | context: {muiTheme: muiTheme},
420 | childContextTypes: {muiTheme: PropTypes.object},
421 | }
422 | );
423 | });
424 |
425 | it('should have column class name', function() {
426 | const headerColumns = wrapper.find(DataTablesHeaderColumn);
427 | expect(headerColumns.getNodes()[0].props.className).to.equal('important-column');
428 | expect(headerColumns.getNodes()[1].props.className).to.equal('important-column');
429 | expect(headerColumns.getNodes()[2].props.className).to.equal(undefined);
430 | });
431 | });
432 |
433 | describe('Programmatically select rows', function() {
434 | let wrapper;
435 | const muiTheme = getMuiTheme();
436 |
437 | before(function() {
438 | // full rendering
439 | wrapper = mount(
440 | ,
453 | {
454 | context: {muiTheme: muiTheme},
455 | childContextTypes: {muiTheme: PropTypes.object},
456 | }
457 | );
458 | });
459 |
460 | it('should render selected rows by default', function() {
461 | const tableRows = wrapper.find(DataTablesRow);
462 | expect(tableRows.getNodes()[0].props.selected).to.equal(true);
463 | expect(tableRows.getNodes()[1].props.selected).to.equal(false);
464 | expect(tableRows.getNodes()[2].props.selected).to.equal(true);
465 | expect(tableRows.getNodes()[3].props.selected).to.equal(false);
466 | expect(tableRows.getNodes()[4].props.selected).to.equal(false);
467 | expect(tableRows.getNodes()[5].props.selected).to.equal(true);
468 | });
469 | });
470 |
471 | describe('onRowSelection handler & onCellDoubleClick handler', function() {
472 | const handleRowSelection = sinon.spy();
473 | const handleCellDoubleClick = sinon.spy();
474 | let wrapper;
475 | const muiTheme = getMuiTheme();
476 |
477 | before(function() {
478 | // full rendering
479 | wrapper = mount(
480 | ,
494 | {
495 | context: {muiTheme: muiTheme},
496 | childContextTypes: {muiTheme: PropTypes.object},
497 | }
498 | );
499 |
500 | // dummy
501 | global.window.getSelection = function() {
502 | return {
503 | removeAllRanges: function() {
504 | },
505 | };
506 | };
507 | });
508 |
509 | it('should call row select handler', function(done) {
510 | wrapper.find(DataTablesRowColumn).first().simulate('click');
511 | setTimeout(() => {
512 | expect(handleRowSelection).to.have.property('callCount', 1);
513 | done();
514 | }, 500);
515 | });
516 |
517 | it('should call dobule click handler', function() {
518 | // simulate double click
519 | wrapper.find(DataTablesRowColumn).first().simulate('click');
520 | wrapper.find(DataTablesRowColumn).first().simulate('click');
521 | expect(handleCellDoubleClick).to.have.property('callCount', 1);
522 | });
523 | });
524 |
525 | describe('row size controls', function() {
526 | const muiTheme = getMuiTheme();
527 | describe('when showRowSizeControls = false', function() {
528 | let wrapper;
529 | before(function() {
530 | wrapper = mount(
531 | ,
544 | {
545 | context: {muiTheme: muiTheme},
546 | childContextTypes: {muiTheme: PropTypes.object},
547 | }
548 | );
549 | });
550 |
551 | it('should not render row size label', function() {
552 | expect(wrapper.find({style: styles.footerToolbarItem})).to.have.length(2);
553 | });
554 |
555 | it('should not render row size menu', function() {
556 | expect(wrapper.find(DropDownMenu)).to.have.length(0);
557 | });
558 |
559 | it('should not render any row size items', function() {
560 | const rowSizeItems = wrapper.find(MenuItem);
561 | expect(rowSizeItems).to.have.length(0);
562 | });
563 | });
564 |
565 | describe( 'when rowSizeList is empty', function() {
566 | let wrapper;
567 | before(function() {
568 | wrapper = mount(
569 | ,
583 | {
584 | context: {muiTheme: muiTheme},
585 | childContextTypes: {muiTheme: PropTypes.object},
586 | }
587 | );
588 | });
589 |
590 | it('should render row size label', function() {
591 | expect(wrapper.find({style: styles.footerToolbarItem})).to.have.length(3);
592 | });
593 |
594 | it('should not render row size menu', function() {
595 | expect(wrapper.find(DropDownMenu)).to.have.length(0);
596 | });
597 | });
598 | });
599 |
600 | describe('Disable footer toolbar', function() {
601 | let wrapper;
602 | const muiTheme = getMuiTheme();
603 |
604 | before(function() {
605 | // full rendering
606 | wrapper = mount(
607 | ,
619 | {
620 | context: {muiTheme: muiTheme},
621 | childContextTypes: {muiTheme: PropTypes.object},
622 | }
623 | );
624 | });
625 |
626 | it('should not render footer toolbar', function() {
627 | expect(wrapper.find({style: styles.footerToolbar})).to.have.length(0);
628 | });
629 | });
630 |
631 | describe('Header toolbar mode', function() {
632 | let wrapper;
633 | const muiTheme = getMuiTheme();
634 |
635 | before(function() {
636 | // full rendering
637 | wrapper = mount(
638 | ,
652 | {
653 | context: {muiTheme: muiTheme},
654 | childContextTypes: {muiTheme: PropTypes.object},
655 | }
656 | );
657 | });
658 |
659 | it('should render header toolbar with filter mode', function() {
660 | expect(wrapper.find(DataTablesHeaderToolbar).prop('mode')).to.equal('filter');
661 | expect(wrapper.find(DataTablesHeaderToolbar).prop('filterValue')).to.equal('test');
662 | });
663 | });
664 |
665 | describe('Hide filter icon', function() {
666 | let wrapper;
667 | const muiTheme = getMuiTheme();
668 |
669 | before(function() {
670 | // full rendering
671 | wrapper = mount(
672 | ,
685 | {
686 | context: {muiTheme: muiTheme},
687 | childContextTypes: {muiTheme: PropTypes.object},
688 | }
689 | );
690 | });
691 |
692 | it('should not render filter icon in header toolbar', function() {
693 | expect(wrapper.find(DataTablesHeaderToolbar).find(FilterListIcon)).to.have.length(0);
694 | });
695 | });
696 | });
697 |
--------------------------------------------------------------------------------
/test/DataTables/tableSettings.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {deepOrange500} from 'material-ui/styles/colors';
3 |
4 | export const TABLE_COLUMNS = [
5 | {
6 | key: 'name',
7 | label: 'Dessert (100g serving)',
8 | }, {
9 | key: 'calories',
10 | label: 'Calories',
11 | }, {
12 | key: 'fat',
13 | label: 'Fat (g)',
14 | }, {
15 | key: 'carbs',
16 | label: 'Carbs (g)',
17 | }, {
18 | key: 'protein',
19 | label: 'Protein (g)',
20 | }, {
21 | key: 'sodium',
22 | label: 'Sodium (mg)',
23 | }, {
24 | key: 'calcium',
25 | label: 'Calcium (%)',
26 | }, {
27 | key: 'iron',
28 | label: 'Iron (%)',
29 | },
30 | ];
31 |
32 | export const TABLE_COLUMNS_TOOLTIP = [
33 | {
34 | key: 'name',
35 | label: 'Dessert (100g serving)',
36 | tooltip: 'Dessert (100g serving)',
37 | }, {
38 | key: 'calories',
39 | label: 'Calories',
40 | tooltip: 'Calories',
41 | }, {
42 | key: 'fat',
43 | label: 'Fat (g)',
44 | tooltip: 'Fat (g)',
45 | }, {
46 | key: 'carbs',
47 | label: 'Carbs (g)',
48 | tooltip: 'Carbs (g)',
49 | }, {
50 | key: 'protein',
51 | label: 'Protein (g)',
52 | tooltip: 'Protein (g)',
53 | }, {
54 | key: 'sodium',
55 | label: 'Sodium (mg)',
56 | tooltip: 'Sodium (mg)',
57 | }, {
58 | key: 'calcium',
59 | label: 'Calcium (%)',
60 | tooltip: 'Calcium (%)',
61 | }, {
62 | key: 'iron',
63 | label: 'Iron (%)',
64 | tooltip: 'Iron (%)',
65 | },
66 | ];
67 |
68 | export const TABLE_COLUMNS_SORT_STYLE = [
69 | {
70 | key: 'name',
71 | label: 'Dessert (100g serving)',
72 | sortable: true,
73 | style: {
74 | width: 250,
75 | },
76 | }, {
77 | key: 'calories',
78 | label: 'Calories',
79 | sortable: true,
80 | }, {
81 | key: 'fat',
82 | label: 'Fat (g)',
83 | render: (fat) => {fat}
84 | }, {
85 | key: 'carbs',
86 | label: 'Carbs (g)',
87 | }, {
88 | key: 'protein',
89 | label: 'Protein (g)',
90 | }, {
91 | key: 'sodium',
92 | label: 'Sodium (mg)',
93 | }, {
94 | key: 'calcium',
95 | label: 'Calcium (%)',
96 | }, {
97 | key: 'iron',
98 | label: 'Iron (%)',
99 | },
100 | ];
101 |
102 | export const TABLE_COLUMNS_CLASSNAME = [
103 | {
104 | key: 'name',
105 | label: 'Dessert (100g serving)',
106 | className: 'important-column',
107 | }, {
108 | key: 'calories',
109 | label: 'Calories',
110 | className: 'important-column',
111 | }, {
112 | key: 'fat',
113 | label: 'Fat (g)',
114 | }, {
115 | key: 'carbs',
116 | label: 'Carbs (g)',
117 | }, {
118 | key: 'protein',
119 | label: 'Protein (g)',
120 | }, {
121 | key: 'sodium',
122 | label: 'Sodium (mg)',
123 | }, {
124 | key: 'calcium',
125 | label: 'Calcium (%)',
126 | }, {
127 | key: 'iron',
128 | label: 'Iron (%)',
129 | },
130 | ];
131 |
132 | export const TABLE_DATA = [
133 | {
134 | name: 'Frozen yogurt',
135 | calories: '159',
136 | fat: '6.0',
137 | carbs: '24',
138 | protein: '4.0',
139 | sodium: '87',
140 | calcium: '14%',
141 | iron: '1%',
142 | }, {
143 | name: 'Ice cream sandwich',
144 | calories: '159',
145 | fat: '6.0',
146 | carbs: '24',
147 | protein: '4.0',
148 | sodium: '87',
149 | calcium: '14%',
150 | iron: '1%',
151 | }, {
152 | name: 'Eclair',
153 | calories: '159',
154 | fat: '6.0',
155 | carbs: '24',
156 | protein: '4.0',
157 | sodium: '87',
158 | calcium: '14%',
159 | iron: '1%',
160 | }, {
161 | name: 'Cupcake',
162 | calories: '159',
163 | fat: '6.0',
164 | carbs: '24',
165 | protein: '4.0',
166 | sodium: '87',
167 | calcium: '14%',
168 | iron: '1%',
169 | }, {
170 | name: 'Gingerbread',
171 | calories: '159',
172 | fat: '6.0',
173 | carbs: '24',
174 | protein: '4.0',
175 | sodium: '87',
176 | calcium: '14%',
177 | iron: '1%',
178 | }, {
179 | name: 'Jelly bean',
180 | calories: '159',
181 | fat: '6.0',
182 | carbs: '24',
183 | protein: '4.0',
184 | sodium: '87',
185 | calcium: '14%',
186 | iron: '1%',
187 | }, {
188 | name: 'Lollipop',
189 | calories: '159',
190 | fat: '6.0',
191 | carbs: '24',
192 | protein: '4.0',
193 | sodium: '87',
194 | calcium: '14%',
195 | iron: '1%',
196 | }, {
197 | name: 'Honeycomb',
198 | calories: '159',
199 | fat: '6.0',
200 | carbs: '24',
201 | protein: '4.0',
202 | sodium: '87',
203 | calcium: '14%',
204 | iron: '1%',
205 | }, {
206 | name: 'Donut',
207 | calories: '159',
208 | fat: '6.0',
209 | carbs: '24',
210 | protein: '4.0',
211 | sodium: '87',
212 | calcium: '14%',
213 | iron: '1%',
214 | }, {
215 | name: 'KitKat',
216 | calories: '159',
217 | fat: '6.0',
218 | carbs: '24',
219 | protein: '4.0',
220 | sodium: '87',
221 | calcium: '14%',
222 | iron: '1%',
223 | },
224 | ];
225 |
226 | export const styles = {
227 | footerToolbarItem: {
228 | marginLeft: 8,
229 | marginRight: 8,
230 | alignItems: 'center',
231 | display: 'flex',
232 | },
233 | titleStyle: {
234 | fontSize: 16,
235 | color: deepOrange500,
236 | },
237 | footerToolbarStyle: {
238 | padding: '0 100px',
239 | },
240 | tableStyle: {
241 | tableLayout: 'auto',
242 | },
243 | tableBodyStyle: {
244 | overflowX: 'auto',
245 | },
246 | tableWrapperStyle: {
247 | padding: 5,
248 | },
249 | };
250 |
--------------------------------------------------------------------------------