├── .babelrc
├── .coveralls.yml
├── .eslintrc
├── .firebaserc
├── .github
└── ISSUE_TEMPLATE.md
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── docs
├── icons
│ └── GitHub.js
├── pages
│ ├── _document.js
│ └── index.js
├── static
│ ├── favicon.ico
│ ├── header.png
│ └── mui-datatables-main.jpg
├── utils
│ ├── CodeSnippet.js
│ ├── Menu.js
│ ├── getPageContext.js
│ ├── layout.js
│ └── withRoot.js
└── v2_to_v3_guide.md
├── examples
├── Router
│ ├── ExamplesGrid.js
│ └── index.js
├── array-value-columns
│ └── index.js
├── column-filters
│ └── index.js
├── column-options-update
│ └── index.js
├── column-sort
│ └── index.js
├── component
│ ├── cities.js
│ └── index.js
├── csv-export
│ └── index.js
├── custom-action-columns
│ └── index.js
├── custom-components
│ ├── TableViewCol.js
│ └── index.js
├── customize-columns
│ └── index.js
├── customize-filter
│ └── index.js
├── customize-footer
│ ├── CustomFooter.js
│ └── index.js
├── customize-rows
│ └── index.js
├── customize-search-render
│ ├── CustomSearchRender.js
│ └── index.js
├── customize-search
│ └── index.js
├── customize-sorting
│ └── index.js
├── customize-styling
│ └── index.js
├── customize-toolbar-icons
│ └── index.js
├── customize-toolbar
│ ├── CustomToolbar.js
│ └── index.js
├── customize-toolbarselect
│ ├── CustomToolbarSelect.js
│ └── index.js
├── data-as-objects
│ └── index.js
├── draggable-columns
│ └── index.js
├── examples.js
├── expandable-rows
│ └── index.js
├── fixed-header
│ └── index.js
├── hide-columns-print
│ └── index.js
├── infinite-scrolling
│ └── index.js
├── large-data-set
│ └── index.js
├── on-download
│ └── index.js
├── on-table-init
│ └── index.js
├── resizable-columns
│ └── index.js
├── selectable-rows
│ └── index.js
├── serverside-filters
│ └── index.js
├── serverside-pagination
│ └── index.js
├── serverside-sorting
│ ├── cities.js
│ └── index.js
├── simple-no-toolbar
│ └── index.js
├── simple
│ └── index.js
├── text-localization
│ └── index.js
└── themes
│ └── index.js
├── firebase.json
├── index.html
├── next.config.js
├── package-lock.json
├── package.json
├── prettier.config.js
├── rollup.config.js
├── src
├── MUIDataTable.js
├── components
│ ├── ExpandButton.js
│ ├── JumpToPage.js
│ ├── Popover.js
│ ├── TableBody.js
│ ├── TableBodyCell.js
│ ├── TableBodyRow.js
│ ├── TableFilter.js
│ ├── TableFilterList.js
│ ├── TableFilterListItem.js
│ ├── TableFooter.js
│ ├── TableHead.js
│ ├── TableHeadCell.js
│ ├── TableHeadRow.js
│ ├── TablePagination.js
│ ├── TableResize.js
│ ├── TableSearch.js
│ ├── TableSelectCell.js
│ ├── TableToolbar.js
│ ├── TableToolbarSelect.js
│ └── TableViewCol.js
├── hooks
│ └── useColumnDrop.js
├── index.js
├── localStorage
│ ├── index.js
│ ├── load.js
│ └── save.js
├── plug-ins
│ └── DebounceSearchRender.js
├── textLabels.js
└── utils.js
├── test
├── MUIDataTable.test.js
├── MUIDataTableBody.test.js
├── MUIDataTableBodyCell.test.js
├── MUIDataTableCustomComponents.test.js
├── MUIDataTableFilter.test.js
├── MUIDataTableFilterList.test.js
├── MUIDataTableFooter.test.js
├── MUIDataTableHead.test.js
├── MUIDataTableHeadCell.test.js
├── MUIDataTablePagination.test.js
├── MUIDataTableSearch.test.js
├── MUIDataTableSelectCell.test.js
├── MUIDataTableToolbar.test.js
├── MUIDataTableToolbarCustomIcons.test.js
├── MUIDataTableToolbarSelect.test.js
├── MUIDataTableViewCol.test.js
├── TableResize.test.js
├── UseColumnDrop.test.js
├── mocha.opts
├── setup-mocha-env.js
└── utils.test.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "test": {
4 | "plugins": [
5 | "istanbul"
6 | ]
7 | }
8 | },
9 | "presets": [
10 | [
11 | "@babel/preset-env",
12 | {
13 | "targets": {
14 | "browsers": [
15 | "ie >= 11",
16 | "> 1%",
17 | "iOS >= 8",
18 | "Android >= 4"
19 | ],
20 | "node": "6.10"
21 | },
22 | "useBuiltIns": "entry",
23 | "debug": false,
24 | "modules": false,
25 | "corejs": {
26 | "version": 3,
27 | "proposals": true
28 | }
29 | }
30 | ],
31 | [
32 | "@babel/preset-react"
33 | ]
34 | ],
35 | "plugins": [
36 | [
37 | "@babel/plugin-proposal-class-properties"
38 | ],
39 | [
40 | "@babel/plugin-proposal-object-rest-spread"
41 | ],
42 | [
43 | "@babel/plugin-transform-async-to-generator"
44 | ],
45 | [
46 | "@babel/plugin-transform-runtime",
47 | {
48 | "corejs": 3,
49 | "regenerator": true
50 | }
51 | ]
52 | ]
53 | }
54 |
--------------------------------------------------------------------------------
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | repo_token: mUu8CcBb7ZPxJvlzfjykPQjABLh52Gxob
2 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "settings": {
4 | "react": {
5 | "version": "latest"
6 | },
7 | "import/extensions": [
8 | ".js"
9 | ],
10 | "import/parser": "babel-eslint",
11 | "import/resolver": {
12 | "node": {
13 | "extensions": [
14 | ".js"
15 | ]
16 | },
17 | "webpack": {
18 | "config": "webpack.config.js"
19 | }
20 | }
21 | },
22 | "parserOptions": {
23 | "ecmaVersion": 6,
24 | "sourceType": "module",
25 | "allowImportExportEverywhere": true,
26 | "ecmaFeatures": {
27 | "jsx": true,
28 | "experimentalObjectRestSpread": true
29 | }
30 | },
31 | "env": {
32 | "es6": true,
33 | "browser": true,
34 | "mocha": true,
35 | "node": true
36 | },
37 | "extends": [
38 | "plugin:jsx-a11y/recommended"
39 | ],
40 | "rules": {
41 | "no-console": "off",
42 | "semi": 2,
43 | "no-undef": 2,
44 | "no-undef-init": 2,
45 | "no-tabs": 2,
46 | "react/self-closing-comp": 2,
47 | "react/no-typos": 2,
48 | "react/jsx-no-duplicate-props": "warn",
49 | "react-hooks/rules-of-hooks": "error",
50 | "react-hooks/exhaustive-deps": "warn",
51 | "jsx-a11y/no-autofocus": [
52 | 2,
53 | {
54 | "ignoreNonDOM": true
55 | }
56 | ]
57 | },
58 | "plugins": [
59 | "import",
60 | "react",
61 | "jsx-a11y",
62 | "filenames",
63 | "react-hooks"
64 | ]
65 | }
66 |
--------------------------------------------------------------------------------
/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "materialui-datatables"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 | ## Expected Behavior
8 |
12 |
13 | ## Current Behavior
14 |
18 |
19 | ## Steps to Reproduce (for bugs)
20 |
24 |
25 | 1.
26 | 2.
27 | 3.
28 | 4.
29 |
30 | ## Your Environment
31 |
32 |
33 | | Tech | Version |
34 | |--------------|---------|
35 | | Material-UI | |
36 | | MUI-datatables | |
37 | | React | |
38 | | browser | |
39 | | etc | |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #Osx
2 | .DS_Store
3 |
4 | # Logs
5 | logs
6 | *.log
7 | npm-debug.log*
8 | yarn-debug.log*
9 | yarn-error.log*
10 |
11 | # Diagnostic reports (https://nodejs.org/api/report.html)
12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
13 |
14 | # sublime project
15 | *.sublime-project
16 | *.sublime-workspace
17 |
18 | # vscode project
19 | .vscode
20 |
21 | # idea project
22 | .idea
23 |
24 | # Runtime data
25 | pids
26 | *.pid
27 | *.seed
28 | *.pid.lock
29 |
30 | # Directory for instrumented libs generated by jscoverage/JSCover
31 | lib-cov
32 |
33 | # Coverage directory used by tools like istanbul
34 | coverage
35 | .nyc_output/
36 | *.lcov
37 |
38 | # Compiled binary addons (http://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directory
42 | node_modules
43 |
44 | # Optional npm cache directory
45 | .npm
46 |
47 | # Optional eslint cache
48 | .eslintcache
49 |
50 | # Build Data
51 | dist/*
52 | es/*
53 | lib/*
54 |
55 | # Docs
56 | docs/.next/*
57 | docs/export/*
58 | .firebase/
59 |
60 | # Optional REPL history
61 | .node_repl_history
62 |
63 | ### VisualStudioCode Patch ###
64 | # Ignore all local history of files
65 | .history
66 |
67 | # Yarn Integrity file
68 | .yarn-integrity
69 |
70 | # Yarn lock
71 | # Note:
72 | # Remove the line below when we switched to Yarn
73 | yarn.lock
74 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "node"
4 |
5 | script:
6 | - npm run -s lint
7 | - npm run -s coverage
8 | - npm run -s build
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 gregn
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 |
--------------------------------------------------------------------------------
/docs/icons/GitHub.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable max-len */
2 |
3 | import React from 'react';
4 | import SvgIcon from '@mui/material/SvgIcon';
5 |
6 | function GitHub(props) {
7 | return (
8 |
9 |
10 |
11 | );
12 | }
13 |
14 | GitHub.muiName = 'SvgIcon';
15 |
16 | export default GitHub;
17 |
--------------------------------------------------------------------------------
/docs/pages/_document.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Document, { Head, Main, NextScript } from 'next/document';
3 | import JssProvider from 'react-jss/lib/JssProvider';
4 | import getPageContext from '../utils/getPageContext';
5 |
6 | class MyDocument extends Document {
7 | render() {
8 | return (
9 |
10 |
11 | Material-UI DataTables
12 |
13 |
14 |
15 |
16 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
54 |
55 |
56 |
67 |
68 |
69 | {this.props.customValue}
70 |
71 |
72 |
73 |
74 | );
75 | }
76 | }
77 |
78 | MyDocument.getInitialProps = ctx => {
79 | const pageContext = getPageContext();
80 | const page = ctx.renderPage(Component => props => (
81 |
82 |
83 |
84 | ));
85 |
86 | return {
87 | ...page,
88 | pageContext,
89 | styles: (
90 |
95 | ),
96 | };
97 | };
98 |
99 | export default MyDocument;
100 |
--------------------------------------------------------------------------------
/docs/pages/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Typography from '@mui/material/Typography';
4 | import IconButton from '@mui/material/IconButton';
5 | import Tooltip from '@mui/material/Tooltip';
6 | import DownloadIcon from '@mui/icons-material/CloudDownload';
7 | import BuildIcon from '@mui/icons-material/Build'; // eslint-disable-line import/no-unresolved
8 | import CodeSnippet from '../utils/CodeSnippet';
9 | import Layout from '../utils/layout';
10 | import withRoot from '../utils/withRoot';
11 | import { withStyles } from 'tss-react/mui';
12 |
13 | const styles = theme => ({
14 | stepIcon: {
15 | fontSize: '30px',
16 | marginRight: theme.spacing.unit * 2,
17 | },
18 | stepWrapper: {
19 | marginTop: theme.spacing.unit * 4,
20 | display: 'flex',
21 | alignItems: 'center',
22 | },
23 | mainImage: {
24 | maxWidth: '100%',
25 | },
26 | });
27 |
28 | class Homepage extends React.Component {
29 | render() {
30 | const { classes } = this.props;
31 |
32 | return (
33 |
34 |
35 |
36 | MUI-Datatables is a data tables component built on Material-UI V3 .
37 | It comes with features like filtering, view/hide columns, search, export to CSV download, printing,
38 | selectable rows, pagination, and sorting. On top of the ability to customize styling on most views, there
39 | are two responsive modes "stacked" and "scroll" for mobile/tablet devices.
40 |
41 |
47 |
48 |
49 |
50 | Installation
51 |
52 |
53 |
54 |
55 |
56 | Usage
57 |
58 |
`}
81 | />
82 |
83 |
84 | );
85 | }
86 | }
87 |
88 | export default withRoot(withStyles(Homepage, styles));
89 |
--------------------------------------------------------------------------------
/docs/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gregnb/mui-datatables/e09c39cb3c5713c7c157845b3d3e4b7d77c77649/docs/static/favicon.ico
--------------------------------------------------------------------------------
/docs/static/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gregnb/mui-datatables/e09c39cb3c5713c7c157845b3d3e4b7d77c77649/docs/static/header.png
--------------------------------------------------------------------------------
/docs/static/mui-datatables-main.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gregnb/mui-datatables/e09c39cb3c5713c7c157845b3d3e4b7d77c77649/docs/static/mui-datatables-main.jpg
--------------------------------------------------------------------------------
/docs/utils/CodeSnippet.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import clsx from 'clsx';
4 | import prism from 'prismjs';
5 | import 'prismjs/components/prism-jsx';
6 | import 'prismjs/components/prism-bash';
7 | import Paper from '@mui/material/Paper';
8 | import { withStyles } from 'tss-react/mui';
9 |
10 | const styles = theme => ({});
11 |
12 | class CodeSnippet extends React.Component {
13 | static propTypes = {
14 | classes: PropTypes.object.isRequired,
15 | language: PropTypes.string,
16 | text: PropTypes.string.isRequired,
17 | };
18 |
19 | static defaultProps = {
20 | language: 'jsx',
21 | };
22 |
23 | render() {
24 | const { classes, language, text } = this.props;
25 | const hightlightedCode = prism.highlight(text, prism.languages[language]);
26 |
27 | return (
28 |
29 |
30 |
31 |
32 |
33 | );
34 | }
35 | }
36 |
37 | export default withStyles(CodeSnippet, styles);
38 |
--------------------------------------------------------------------------------
/docs/utils/Menu.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { withStyles } from '@mui/material';
4 | import Drawer from '@mui/material/Drawer';
5 | import List from '@mui/material/List';
6 | import ListItem from '@mui/material/ListItem';
7 | import ListItemText from '@mui/material/ListItemText';
8 | import ListSubheader from '@mui/material/ListSubheader';
9 |
10 | const styles = theme => ({
11 | list: {
12 | width: 250,
13 | },
14 | listTitle: {
15 | fontSize: 25,
16 | },
17 | });
18 |
19 | const sandboxes = [
20 | { name: 'Custom Component', href: 'https://codesandbox.io/embed/xrvrzryjvp?autoresize=1&hidenavigation=1' },
21 | { name: 'Customize Columns', href: 'https://codesandbox.io/embed/xowj5oj8w?autoresize=1&hidenavigation=1' },
22 | { name: 'Customize Footer', href: 'https://codesandbox.io/embed/5z0w0w9jyk?autoresize=1&hidenavigation=1' },
23 | { name: 'Customize Styling', href: 'https://codesandbox.io/embed/0ylq1lqwp0?autoresize=1&hidenavigation=1' },
24 | { name: 'Customize Toolbar', href: 'https://codesandbox.io/embed/wy2rl1nyzl?autoresize=1&hidenavigation=1' },
25 | { name: 'Customize ToolbarSelect', href: 'https://codesandbox.io/embed/545ym5ov6p?autoresize=1&hidenavigation=1' },
26 | { name: 'Resizable Columns', href: 'https://codesandbox.io/embed/q8w3230qpj?autoresize=1&hidenavigation=1' },
27 | ];
28 |
29 | const SandboxItem = props => (
30 |
31 | window.open(props.href, '_blank')} primary={props.name} />
32 |
33 | );
34 |
35 | SandboxItem.propTypes = {
36 | href: PropTypes.string,
37 | name: PropTypes.string,
38 | };
39 |
40 | class Menu extends React.Component {
41 | render() {
42 | const { isOpen, toggle, classes } = this.props;
43 | return (
44 |
45 |
46 |
50 | Examples
51 |
52 | }>
53 | {sandboxes.map(item => (
54 |
55 | ))}
56 |
57 |
58 |
59 | );
60 | }
61 | }
62 |
63 | Menu.propTypes = {
64 | isOpen: PropTypes.bool.isRequired,
65 | toggle: PropTypes.func.isRequired,
66 | classes: PropTypes.object.isRequired,
67 | };
68 |
69 | export default withStyles(styles)(Menu);
70 |
--------------------------------------------------------------------------------
/docs/utils/getPageContext.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-underscore-dangle */
2 |
3 | import { SheetsRegistry } from 'jss';
4 | import { createTheme, createGenerateClassName } from '@mui/material/styles';
5 | import purple from '@mui/material/colors/purple';
6 | import green from '@mui/material/colors/green';
7 |
8 | // A theme with custom primary and secondary color.
9 | // It's optional.
10 | const theme = createTheme({
11 | palette: {
12 | primary: purple,
13 | secondary: green,
14 | },
15 | });
16 |
17 | function createPageContext() {
18 | return {
19 | theme,
20 | // This is needed in order to deduplicate the injection of CSS in the page.
21 | sheetsManager: new Map(),
22 | // This is needed in order to inject the critical CSS.
23 | sheetsRegistry: new SheetsRegistry(),
24 | // The standard class name generator.
25 | generateClassName: createGenerateClassName(),
26 | };
27 | }
28 |
29 | export default function getPageContext() {
30 | // Make sure to create a new context for every server-side request so that data
31 | // isn't shared between connections (which would be bad).
32 | if (!process.browser) {
33 | return createPageContext();
34 | }
35 |
36 | // Reuse context on the client-side.
37 | if (!global.__INIT_MATERIAL_UI__) {
38 | global.__INIT_MATERIAL_UI__ = createPageContext();
39 | }
40 |
41 | return global.__INIT_MATERIAL_UI__;
42 | }
43 |
--------------------------------------------------------------------------------
/docs/utils/layout.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Head from 'next/head';
4 | import Typography from '@mui/material/Typography';
5 | import AppBar from '@mui/material/AppBar';
6 | import Toolbar from '@mui/material/Toolbar';
7 | import IconButton from '@mui/material/IconButton';
8 | import MenuIcon from '@mui/icons-material/Menu';
9 | import Tooltip from '@mui/material/Tooltip';
10 | import GitHub from '../icons/GitHub';
11 | import withRoot from '../utils/withRoot';
12 | import { withStyles } from 'tss-react/mui';
13 | import Menu from './Menu';
14 |
15 | /* eslint-disable import/no-webpack-loader-syntax */
16 | import lightTheme from '!raw-loader!prismjs/themes/prism.css';
17 |
18 | const styles = theme => ({
19 | appBar: {
20 | backgroundColor: '#23232f',
21 | },
22 | toolBar: {
23 | justifyContent: 'space-between',
24 | },
25 | logo: {
26 | display: 'block',
27 | height: '56px',
28 | position: 'relative',
29 | top: '5px',
30 | },
31 | wrapper: {
32 | flex: '1 0 100%',
33 | },
34 | content: {
35 | flex: '1 0 100%',
36 | margin: '0 auto',
37 | padding: '16px 16px 0px 16px',
38 | marginTop: '64px',
39 | minHeight: '600px',
40 | maxWidth: '960px',
41 | },
42 | footer: {
43 | flex: '1 0 100%',
44 | marginTop: '32px',
45 | },
46 | });
47 |
48 | class Layout extends React.Component {
49 | state = {
50 | drawerIsOpen: false,
51 | };
52 |
53 | componentDidMount() {
54 | const styleNode = document.createElement('style');
55 | styleNode.setAttribute('data-prism', 'true');
56 | if (document.head) {
57 | document.head.appendChild(styleNode);
58 | }
59 |
60 | styleNode.textContent = lightTheme;
61 | }
62 |
63 | toggleDrawer = () => {
64 | const drawerIsOpen = this.state.drawerIsOpen ? false : true;
65 | this.setState({ drawerIsOpen });
66 | };
67 |
68 | render() {
69 | const { classes, children } = this.props;
70 | const { drawerIsOpen } = this.state;
71 |
72 | return (
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | {children}
96 |
97 |
98 |
99 | );
100 | }
101 | }
102 |
103 | export default withRoot(withStyles(Layout, styles));
104 |
--------------------------------------------------------------------------------
/docs/utils/withRoot.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { ThemeProvider } from '@mui/material/styles';
4 | import getPageContext from './getPageContext';
5 |
6 | function withRoot(Component) {
7 | class WithRoot extends React.Component {
8 | UNSAFE_componentWillMount() {
9 | this.pageContext = this.props.pageContext || getPageContext();
10 | }
11 |
12 | componentDidMount() {
13 | // Remove the server-side injected CSS.
14 | const jssStyles = document.querySelector('#jss-server-side');
15 | if (jssStyles && jssStyles.parentNode) {
16 | jssStyles.parentNode.removeChild(jssStyles);
17 | }
18 | }
19 |
20 | pageContext = null;
21 |
22 | render() {
23 | // ThemeProvider makes the theme available down the React tree thanks to React context.
24 | return (
25 |
26 |
27 |
28 | );
29 | }
30 | }
31 |
32 | WithRoot.propTypes = {
33 | pageContext: PropTypes.object,
34 | };
35 |
36 | WithRoot.getInitialProps = ctx => {
37 | if (Component.getInitialProps) {
38 | return Component.getInitialProps(ctx);
39 | }
40 |
41 | return {};
42 | };
43 |
44 | return WithRoot;
45 | }
46 |
47 | export default withRoot;
48 |
--------------------------------------------------------------------------------
/examples/Router/ExamplesGrid.js:
--------------------------------------------------------------------------------
1 | import {Link} from "react-router-dom";
2 | import {Card, CardContent, Grid, Typography} from "@mui/material";
3 | import React from "react";
4 | import examples from "../examples";
5 | import { withStyles } from "tss-react/mui";
6 | import TextField from '@mui/material/TextField';
7 |
8 | const styles = {
9 | container: {
10 | flexGrow: 1,
11 | justifyContent: 'center',
12 | alignItems: 'center',
13 | marginTop: 16,
14 | },
15 | card: {
16 | '&:hover': {
17 | background: 'lightgrey',
18 | fontWeight: 500,
19 | }
20 | },
21 | cardContent: {
22 | '&:last-child': {
23 | padding: 8,
24 | }
25 | },
26 | link: {
27 | textDecoration: 'none',
28 | },
29 | label: {
30 | fontWeight: 'inherit'
31 | }
32 | };
33 |
34 | class ExamplesGrid extends React.Component {
35 |
36 | state = {
37 | searchVal: ''
38 | }
39 |
40 | setSearchVal = (val) => {
41 | this.setState({
42 | searchVal: val
43 | });
44 | }
45 |
46 | render() {
47 | const {classes} = this.props;
48 |
49 | // Sort Examples alphabetically
50 | const examplesSorted = {};
51 | Object.keys(examples).sort().forEach(function (key) {
52 | examplesSorted[key] = examples[key];
53 | });
54 |
55 | const examplesSortedKeys = Object.keys(examplesSorted).filter((item) => {
56 | if (this.state.searchVal === '') return true;
57 | console.dir(item);
58 | return item.toLowerCase().indexOf( this.state.searchVal.toLowerCase() ) !== -1 ? true : false;
59 | });
60 |
61 | return (
62 |
63 | Choose an Example
64 | ({examplesSortedKeys.length}) Examples
65 |
66 |
67 | this.setSearchVal(e.target.value)} />
68 |
69 |
70 |
71 | {examplesSortedKeys.map((label, index) => (
72 |
73 |
74 |
75 |
76 | {label}
77 |
78 |
79 |
80 |
81 | ))}
82 |
83 |
84 | );
85 | }
86 | }
87 |
88 | export default withStyles(ExamplesGrid, styles);
89 |
--------------------------------------------------------------------------------
/examples/Router/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 | import { HashRouter as Router, Route, Switch, withRouter } from 'react-router-dom';
4 | import { withStyles } from 'tss-react/mui';
5 | import ExamplesGrid from './ExamplesGrid';
6 | import examples from '../examples';
7 | import Button from '@mui/material/Button';
8 | import { createTheme, ThemeProvider } from '@mui/material/styles';
9 |
10 | const styles = {
11 | root: {
12 | display: 'flex',
13 | justifyContent: 'center',
14 | },
15 | contentWrapper: {
16 | width: '100%',
17 | },
18 | };
19 |
20 | class Examples extends React.Component {
21 | returnHome = () => {
22 | this.props.history.push('/');
23 | };
24 |
25 | render() {
26 | const { classes } = this.props;
27 |
28 | var returnHomeStyle = { padding: '0px', margin: '20px 0 20px 0' };
29 |
30 | const defaultTheme = createTheme();
31 |
32 | return (
33 |
34 |
35 |
36 |
37 | } />
38 | {Object.keys(examples).map((label, index) => (
39 |
45 | ))}
46 |
47 |
48 | {this.props.location.pathname !== '/' && (
49 |
50 |
51 | Back to Example Index
52 |
53 |
54 | )}
55 |
56 |
57 |
58 |
59 | );
60 | }
61 | }
62 |
63 | const StyledExamples = withRouter(withStyles(Examples, styles));
64 |
65 | function App() {
66 | return (
67 |
68 |
69 |
70 | );
71 | }
72 | const container = document.getElementById('app-root');
73 | const root = createRoot(container);
74 | root.render( );
75 |
--------------------------------------------------------------------------------
/examples/column-filters/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import MUIDataTable from "../../src/";
4 |
5 | class Example extends React.Component {
6 |
7 | render() {
8 |
9 | const columns = [
10 | {
11 | name: "Name",
12 | options: {
13 | filter: true,
14 | filterList: ['Franky Miles'],
15 | customFilterListOptions: { render: v => `Name: ${v}` },
16 | filterOptions: {
17 | names: ['a', 'b', 'c', 'Business Analyst']
18 | },
19 | }
20 | },
21 | {
22 | name: "Title",
23 | options: {
24 | filter: true,
25 | filterList: ['Business Analyst'],
26 | customFilterListOptions: { render: v => `Title: ${v}` },
27 | filterType: 'textField' // set filterType's at the column level
28 | }
29 | },
30 | {
31 | name: "Location",
32 | options: {
33 | filter: false,
34 | }
35 | },
36 | {
37 | name: "Age",
38 | options: {
39 | customBodyRenderLite: (dataIndex) => {
40 | let val = data[dataIndex][3];
41 | return val;
42 | },
43 | filter: true,
44 | customFilterListOptions: { render: v => `Age: ${v}` },
45 | }
46 | },
47 | {
48 | name: "Salary",
49 | options: {
50 | filter: true,
51 | customFilterListOptions: { render: v => `Salary: ${v}` },
52 | sort: false
53 | }
54 | }
55 | ];
56 | const data = [
57 | ["Gabby George", "Business Analyst", "Minneapolis", 30, 100000],
58 | ["Business Analyst", "Business Consultant", "Dallas", 55, 200000],
59 | ["Jaden Collins", "Attorney", "Santa Ana", 27, 500000],
60 | ["Franky Rees", "Business Analyst", "St. Petersburg", 22, 50000],
61 | ["Aaren Rose", "Business Consultant", "Toledo", 28, 75000],
62 | ["Blake Duncan", "Business Management Analyst", "San Diego", 65, 94000],
63 | ["Frankie Parry", "Agency Legal Counsel", "Jacksonville", 71, 210000],
64 | ["Lane Wilson", "Commercial Specialist", "Omaha", 19, 65000],
65 | ["Robin Duncan", "Business Analyst", "Los Angeles", 20, 77000],
66 | ["Mel Brooks", "Business Consultant", "Oklahoma City", 37, 135000],
67 | ["Harper White", "Attorney", "Pittsburgh", 52, 420000],
68 | ["Kris Humphrey", "Agency Legal Counsel", "Laredo", 30, 150000],
69 | ["Frankie Long", "Industrial Analyst", "Austin", 31, 170000],
70 | ["Brynn Robbins", "Business Analyst", "Norfolk", 22, 90000],
71 | ["Justice Mann", "Business Consultant", "Chicago", 24, 133000],
72 | ["Addison Navarro", "Business Management Analyst", "New York", 50, 295000],
73 | ["Jesse Welch", "Agency Legal Counsel", "Seattle", 28, 200000],
74 | ["Eli Mejia", "Commercial Specialist", "Long Beach", 65, 400000],
75 | ["Gene Leblanc", "Industrial Analyst", "Hartford", 34, 110000],
76 | ["Danny Leon", "Computer Scientist", "Newark", 60, 220000],
77 | ["Lane Lee", "Corporate Counselor", "Cincinnati", 52, 180000],
78 | ["Jesse Hall", "Business Analyst", "Baltimore", 44, 99000],
79 | ["Danni Hudson", "Agency Legal Counsel", "Tampa", 37, 90000],
80 | ["Terry Macdonald", "Commercial Specialist", "Miami", 39, 140000],
81 | ["Justice Mccarthy", "Attorney", "Tucson", 26, 330000],
82 | ["Silver Carey", "Computer Scientist", "Memphis", 47, 250000],
83 | ["Franky Miles", "Industrial Analyst", "Buffalo", 49, 190000],
84 | ["Glen Nixon", "Corporate Counselor", "Arlington", 44, 80000],
85 | ["Gabby Strickland", "Business Process Consultant", "Scottsdale", 26, 45000],
86 | ["Mason Ray", "Computer Scientist", "San Francisco", 39, 142000]
87 | ];
88 |
89 | const options = {
90 | filter: true,
91 | onFilterChange: (changedColumn, filterList) => {
92 | console.log(changedColumn, filterList);
93 | },
94 | selectableRows: 'multiple',
95 | filterType: 'dropdown',
96 | responsive: 'vertical',
97 | rowsPerPage: 10,
98 | };
99 |
100 | return (
101 |
102 | );
103 |
104 | }
105 | }
106 |
107 | export default Example;
108 |
--------------------------------------------------------------------------------
/examples/column-sort/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import { Chip } from "@mui/material";
4 | import MUIDataTable from "../../src/";
5 |
6 | class Example extends React.Component {
7 |
8 | render() {
9 |
10 | const columns = [
11 | {
12 | name: "Name",
13 | },
14 | {
15 | name: "Title",
16 | },
17 | {
18 | name: "Age",
19 | options: {
20 | /*
21 | In this case, age is a string, but we want to compare it as if it was a number.
22 | If you comment out the sortCompare method, you'll see how sorting as a string
23 | is different than sorting as a number. Typically an age field would be a number
24 | so we wouldn't need to write a function like this. But the sortCompare is available
25 | if you run into a situation like this.
26 | */
27 | sortCompare: (order) => {
28 | return (obj1, obj2) => {
29 | console.log(order);
30 | let val1 = parseInt(obj1.data, 10);
31 | let val2 = parseInt(obj2.data, 10);
32 | return (val1 - val2) * (order === 'asc' ? 1 : -1);
33 | };
34 | }
35 | }
36 | },
37 | {
38 | name: "Hobbies",
39 | options: {
40 | sortCompare: (order) =>
41 | ({ data: hobbyList1 }, { data: hobbyList2 }) =>
42 | (hobbyList1.length - hobbyList2.length) * (order === 'asc' ? 1 : -1),
43 | hint: 'Sort by amount of hobbies',
44 | customBodyRender: hobbies => hobbies.map((hobby, index) => )
45 | }
46 | }
47 | ];
48 | const data = [
49 | ["Gabby George", "Business Analyst", "30", ["Sports"]],
50 | ["Business Analyst", "Business Consultant", "55", ["Water Polo"]],
51 | ["Jaden Collins", "Attorney", "27", ["Sports", "TV"]],
52 | ["Franky Rees", "Business Analyst", "22", ["Baking", "Hiking"]],
53 | ["Aaren Rose", "Business Consultant", "28", ["Hiking"]],
54 | ["Blake Duncan", "Business Management Analyst", "65", ["Sprots", "Cooking", "Baking"]],
55 | ["Frankie Parry", "Agency Legal Counsel", "71", []],
56 | ["Lane Wilson", "Commercial Specialist", "19", ["Cooking"]],
57 | ["Robin Duncan", "Business Analyst", "20", ["Acting"]],
58 | ["Mel Brooks", "Business Consultant", "37", ["Puzzles", "Sewing"]],
59 | ["Harper White", "Attorney", "52", ["Fising"]],
60 | ["Kris Humphrey", "Agency Legal Counsel", "30", []],
61 | ["Frankie Long", "Industrial Analyst", "31", []],
62 | ["Brynn Robbins", "Business Analyst", "22", ["Fishing", "Hiking"]],
63 | ["Justice Mann", "Business Consultant", "24", ["Footbal"]],
64 | ["JoJo", "Business Consultant", "2", ["Sleeping"]],
65 | ["Bobby Smith", "Business Consultant", "3", []],
66 | ["Addison Navarro", "Business Management Analyst", "50", ["Photography"]]
67 | ];
68 |
69 | const options = {
70 | filter: true,
71 | filterType: 'dropdown',
72 | responsive: 'vertical',
73 | rowsPerPage: 50,
74 | rowsPerPageOptions: [50],
75 | };
76 |
77 | return (
78 |
79 | );
80 |
81 | }
82 | }
83 |
84 | export default Example;
85 |
--------------------------------------------------------------------------------
/examples/component/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import FormControlLabel from '@mui/material/FormControlLabel';
4 | import TextField from '@mui/material/TextField';
5 | import Switch from '@mui/material/Switch';
6 | import MUIDataTable from "../../src/";
7 | import Cities from "./cities";
8 |
9 | class Example extends React.Component {
10 |
11 | render() {
12 |
13 | const columns = [
14 | {
15 | name: "Name",
16 | options: {
17 | filter: false,
18 | customBodyRender: (value, tableMeta, updateValue) => (
19 | }
23 | onChange={event => updateValue(event.target.value)}
24 | />
25 | )
26 | }
27 | },
28 | {
29 | name: "Title",
30 | options: {
31 | filter: true,
32 | }
33 | },
34 | {
35 | name: "Location",
36 | options: {
37 | filter: true,
38 | customBodyRender: (value, tableMeta, updateValue) => {
39 | return (
40 | updateValue(event)}
44 | />
45 | );
46 | },
47 | }
48 | },
49 | {
50 | name: "Age",
51 | options: {
52 | filter: false,
53 | customBodyRender: (value, tableMeta, updateValue) => (
54 | }
57 | onChange={event => updateValue(event.target.value)}
58 | />
59 | )
60 | }
61 | },
62 | {
63 | name: "Salary",
64 | options: {
65 | filter: true,
66 | customBodyRender: (value, tableMeta, updateValue) => {
67 |
68 | const nf = new Intl.NumberFormat('en-US', {
69 | style: 'currency',
70 | currency: 'USD',
71 | minimumFractionDigits: 2,
72 | maximumFractionDigits: 2
73 | });
74 |
75 | return nf.format(value);
76 | }
77 | }
78 | },
79 | {
80 | name: "Active",
81 | options: {
82 | filter: true,
83 | customBodyRender: (value, tableMeta, updateValue) => {
84 |
85 | return (
86 |
91 | }
92 | onChange={event => {
93 | updateValue(event.target.value === "Yes" ? false : true);
94 | }}
95 | />
96 | );
97 |
98 | }
99 | }
100 | }
101 | ];
102 |
103 | const data = [
104 | ["Robin Duncan", "Business Analyst", "Los Angeles", null, 77000, false],
105 | ["Mel Brooks", "Business Consultant", "Oklahoma City", 37, null, true],
106 | ["Harper White", "Attorney", "Pittsburgh", 52, 420000, false],
107 | ["Kris Humphrey", "Agency Legal Counsel", "Laredo", 30, 150000, true],
108 | ["Frankie Long", "Industrial Analyst", "Austin", 31, 170000, false],
109 | ["Brynn Robbins", "Business Analyst", "Norfolk", 22, 90000, true],
110 | ["Justice Mann", "Business Consultant", "Chicago", 24, 133000, false],
111 | ["Addison Navarro", "Business Management Analyst", "New York", 50, 295000, true],
112 | ["Jesse Welch", "Agency Legal Counsel", "Seattle", 28, 200000, false],
113 | ["Eli Mejia", "Commercial Specialist", "Long Beach", 65, 400000, true],
114 | ["Gene Leblanc", "Industrial Analyst", "Hartford", 34, 110000, false],
115 | ["Danny Leon", "Computer Scientist", "Newark", 60, 220000, true],
116 | ["Lane Lee", "Corporate Counselor", "Cincinnati", 52, 180000, false],
117 | ["Jesse Hall", "Business Analyst", "Baltimore", 44, 99000, true],
118 | ["Danni Hudson", "Agency Legal Counsel", "Tampa", 37, 90000, false],
119 | ["Terry Macdonald", "Commercial Specialist", "Miami", 39, 140000, true],
120 | ["Justice Mccarthy", "Attorney", "Tucson", 26, 330000, false],
121 | ["Silver Carey", "Computer Scientist", "Memphis", 47, 250000, true],
122 | ["Franky Miles", "Industrial Analyst", "Buffalo", 49, 190000, false],
123 | ["Glen Nixon", "Corporate Counselor", "Arlington", 44, 80000, true],
124 | ["Gabby Strickland", "Business Process Consultant", "Scottsdale", 26, 45000, false],
125 | ["Mason Ray", "Computer Scientist", "San Francisco", 39, 142000, true]
126 | ];
127 |
128 | const options = {
129 | filter: true,
130 | filterType: 'dropdown',
131 | responsive: 'standard'
132 | };
133 |
134 | return (
135 |
136 | );
137 |
138 | }
139 | }
140 |
141 | export default Example;
142 |
--------------------------------------------------------------------------------
/examples/custom-components/TableViewCol.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Checkbox from '@mui/material/Checkbox';
4 | import Typography from '@mui/material/Typography';
5 | import Button from '@mui/material/Button';
6 | import FormControl from '@mui/material/FormControl';
7 | import FormGroup from '@mui/material/FormGroup';
8 | import FormControlLabel from '@mui/material/FormControlLabel';
9 | import { makeStyles } from 'tss-react/mui';
10 |
11 | const useStyles = makeStyles({ name: 'MUIDataTableViewCol' })(theme => ({
12 | root: {
13 | padding: '16px 24px 16px 24px',
14 | fontFamily: 'Roboto',
15 | },
16 | title: {
17 | marginLeft: '-7px',
18 | marginRight: '24px',
19 | fontSize: '14px',
20 | color: theme.palette.text.secondary,
21 | textAlign: 'left',
22 | fontWeight: 500,
23 | },
24 | formGroup: {
25 | marginTop: '8px',
26 | },
27 | formControl: {},
28 | checkbox: {
29 | padding: '0px',
30 | width: '32px',
31 | height: '32px',
32 | },
33 | checkboxRoot: {},
34 | checked: {},
35 | label: {
36 | fontSize: '15px',
37 | marginLeft: '8px',
38 | color: theme.palette.text.primary,
39 | },
40 | }));
41 |
42 | const TableViewCol = ({ columns, options, components = {}, onColumnUpdate, updateColumns }) => {
43 | const { classes } = useStyles();
44 | const textLabels = options.textLabels.viewColumns;
45 | const CheckboxComponent = components.Checkbox || Checkbox;
46 |
47 | const handleColChange = index => {
48 | onColumnUpdate(index);
49 | };
50 |
51 | const selectAll = () => {
52 | var newColumns = columns.map(col => {
53 | var newCol = Object.assign({}, col);
54 | newCol.display = 'true';
55 | return newCol;
56 | });
57 | updateColumns(newColumns);
58 | };
59 |
60 | return (
61 |
62 |
63 | {textLabels.title}
64 |
65 |
66 | Show All
67 | {columns.map((column, index) => {
68 | return (
69 | column.display !== 'excluded' &&
70 | column.viewColumns !== false && (
71 | handleColChange(index)}
86 | checked={column.display === 'true'}
87 | value={column.name}
88 | />
89 | }
90 | label={column.label}
91 | />
92 | )
93 | );
94 | })}
95 |
96 |
97 | );
98 | };
99 |
100 | TableViewCol.propTypes = {
101 | /** Columns used to describe table */
102 | columns: PropTypes.array.isRequired,
103 | /** Options used to describe table */
104 | options: PropTypes.object.isRequired,
105 | /** Callback to trigger View column update */
106 | onColumnUpdate: PropTypes.func,
107 | /** Extend the style applied to components */
108 | classes: PropTypes.object,
109 | };
110 |
111 | export default TableViewCol;
112 |
--------------------------------------------------------------------------------
/examples/custom-components/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import MUIDataTable from "../../src/";
3 | import Chip from '@mui/material/Chip';
4 | import Select from '@mui/material/Select';
5 | import MenuItem from '@mui/material/MenuItem';
6 | import TableFilterList from '../../src/components/TableFilterList';
7 | import MuiTooltip from '@mui/material/Tooltip';
8 | import Fade from "@mui/material/Fade";
9 | import Checkbox from '@mui/material/Checkbox';
10 | import Radio from '@mui/material/Radio';
11 | import TableViewCol from './TableViewCol';
12 |
13 | const CustomChip = (props) => {
14 | const { label, onDelete, columnNames, className, index } = props;
15 | return ( );
22 | };
23 |
24 | const CustomTooltip = (props) => {
25 | return (
26 | {props.children}
32 | );
33 | };
34 |
35 | const CustomCheckbox = (props) => {
36 | let newProps = Object.assign({}, props);
37 | newProps.color = props['data-description'] === 'row-select' ? 'secondary' : 'primary';
38 |
39 | if (props['data-description'] === 'row-select') {
40 | return ( );
41 | } else {
42 | return ( );
43 | }
44 | };
45 |
46 | const CustomFilterList = (props) => {
47 | return ;
48 | };
49 |
50 | class Example extends React.Component {
51 |
52 | render() {
53 | const columns = [
54 | { name: 'Name' },
55 | {
56 | name: 'Company',
57 | options: {
58 | filter: true,
59 | filterType: 'custom',
60 | filterList: ['Test Corp'],
61 | customFilterListOptions: {
62 | render: v => {
63 | if (v.length !== 0) {
64 | return `Company: ${v[0]}`;
65 | }
66 | return false;
67 | },
68 | update: (filterList) => filterList,
69 | },
70 | filterOptions: {
71 | names: [],
72 | logic(status, filter) {
73 | if (filter.length > 0) {
74 | return status !== filter[0];
75 | }
76 | return false;
77 | },
78 | display: (filterList, onChange, index, column) => (
79 | {
81 | filterList[index][0] = event.target.value;
82 | onChange(filterList[index], index, column);
83 | }}
84 | value={filterList[index]}
85 | >
86 | {'Test Corp'}
87 | {'Other Corp'}
88 |
89 | )
90 | },
91 | },
92 | },
93 | { name: 'City', label: 'City Label', options: { filterList: ['Dallas'] } },
94 | { name: 'State' },
95 | {
96 | name: 'Empty',
97 | options: {
98 | empty: true,
99 | filterType: 'checkbox',
100 | filterOptions: {
101 | renderValue: (val) => (val ? val : '(Empty)')
102 | }
103 | }
104 | },
105 | ];
106 | const data = [
107 | ['Joe James', 'Test Corp', 'Yonkers', 'NY'],
108 | ['John Walsh', 'Test Corp', 'Hartford', null],
109 | ['Bob Herm', 'Other Corp', 'Tampa', 'FL'],
110 | ['James Houston', 'Test Corp', 'Dallas', 'TX'],
111 | ];
112 |
113 | let options = {
114 | onFilterChipClose: (index, removedFilter, filterList) => {
115 | console.log(index, removedFilter, filterList);
116 | },
117 | selectableRows: 'single',
118 | selectToolbarPlacement: 'none',
119 | };
120 |
121 | return (
122 |
134 | );
135 |
136 | }
137 | }
138 |
139 | export default Example;
140 |
--------------------------------------------------------------------------------
/examples/customize-columns/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import MUIDataTable from "../../src/";
4 |
5 | class Example extends React.Component {
6 |
7 | render() {
8 |
9 | const columns = [
10 | {
11 | name: "Name",
12 | options: {
13 | filter: true,
14 | //display: 'excluded',
15 | sortThirdClickReset: true,
16 | sortDescFirst: true,
17 | }
18 | },
19 | {
20 | label: "Modified Title Label",
21 | name: "Title",
22 | options: {
23 | filter: true,
24 | sortThirdClickReset: true,
25 | }
26 | },
27 | {
28 | name: "Location",
29 | options: {
30 | filter: false,
31 | sortThirdClickReset: true,
32 | customHeadRender: (columnMeta, updateDirection) => (
33 | updateDirection(2)} style={{ cursor: 'pointer' }}>
34 | {columnMeta.name}
35 |
36 | )
37 | }
38 | },
39 | {
40 | name: "Age",
41 | options: {
42 | filter: true,
43 | sortThirdClickReset: true,
44 | }
45 | },
46 | {
47 | name: "Salary",
48 | options: {
49 | filter: true,
50 | sort: true,
51 | sortThirdClickReset: true,
52 | sortDescFirst: true,
53 | sortCompare: (order) => {
54 | return (obj1, obj2) => {
55 | var val1 = parseInt(obj1.data.substr(1).replace(/,/g,''), 10);
56 | var val2 = parseInt(obj2.data.substr(1).replace(/,/g,''), 10);
57 | return (val1 - val2) * (order === 'asc' ? 1 : -1);
58 | };
59 | }
60 | }
61 | }
62 | ];
63 |
64 |
65 | const data = [
66 | ["Gabby George", "Business Analyst", "Minneapolis", 30, "$100,000"],
67 | ["Aiden Lloyd", "Business Consultant", "Dallas", 55, "$200,000"],
68 | ["Jaden Collins", "Attorney", "Santa Ana", 27, "$500,000"],
69 | ["Franky Rees", "Business Analyst", "St. Petersburg", 22, "$50,000"],
70 | ["Aaren Rose", "Business Consultant", "Toledo", 28, "$75,000"],
71 | ["Blake Duncan", "Business Management Analyst", "San Diego", 65, "$94,000"],
72 | ["Frankie Parry", "Agency Legal Counsel", "Jacksonville", 71, "$210,000"],
73 | ["Lane Wilson", "Commercial Specialist", "Omaha", 19, "$65,000"],
74 | ["Robin Duncan", "Business Analyst", "Los Angeles", 20, "$77,000"],
75 | ["Mel Brooks", "Business Consultant", "Oklahoma City", 37, "$135,000"],
76 | ["Harper White", "Attorney", "Pittsburgh", 52, "$420,000"],
77 | ["Kris Humphrey", "Agency Legal Counsel", "Laredo", 30, "$150,000"],
78 | ["Frankie Long", "Industrial Analyst", "Austin", 31, "$170,000"],
79 | ["Brynn Robbins", "Business Analyst", "Norfolk", 22, "$90,000"],
80 | ["Justice Mann", "Business Consultant", "Chicago", 24, "$133,000"],
81 | ["Addison Navarro", "Business Management Analyst", "New York", 50, "$295,000"],
82 | ["Jesse Welch", "Agency Legal Counsel", "Seattle", 28, "$200,000"],
83 | ["Eli Mejia", "Commercial Specialist", "Long Beach", 65, "$400,000"],
84 | ["Gene Leblanc", "Industrial Analyst", "Hartford", 34, "$110,000"],
85 | ["Danny Leon", "Computer Scientist", "Newark", 60, "$220,000"],
86 | ["Lane Lee", "Corporate Counselor", "Cincinnati", 52, "$180,000"],
87 | ["Jesse Hall", "Business Analyst", "Baltimore", 44, "$99,000"],
88 | ["Danni Hudson", "Agency Legal Counsel", "Tampa", 37, "$90,000"],
89 | ["Terry Macdonald", "Commercial Specialist", "Miami", 39, "$140,000"],
90 | ["Justice Mccarthy", "Attorney", "Tucson", 26, "$330,000"],
91 | ["Silver Carey", "Computer Scientist", "Memphis", 47, "$250,000" ],
92 | ["Franky Miles", "Industrial Analyst", "Buffalo", 49, "$190,000"],
93 | ["Glen Nixon", "Corporate Counselor", "Arlington", 44, "$80,000"],
94 | ["Gabby Strickland", "Business Process Consultant", "Scottsdale", 26, "$45,000"],
95 | ["Mason Ray", "Computer Scientist", "San Francisco", 39, "$142,000"]
96 | ];
97 |
98 | const options = {
99 | filter: true,
100 | filterType: 'dropdown',
101 | responsive: 'vertical',
102 | sortOrder: {
103 | name: 'Title',
104 | direction: 'asc'
105 | }
106 | };
107 |
108 | return (
109 |
110 | );
111 |
112 | }
113 | }
114 |
115 | export default Example;
116 |
--------------------------------------------------------------------------------
/examples/customize-footer/CustomFooter.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import TableFooter from "@mui/material/TableFooter";
3 | import TableRow from "@mui/material/TableRow";
4 | import TableCell from "@mui/material/TableCell";
5 | import MuiTablePagination from "@mui/material/TablePagination";
6 | import { withStyles } from "tss-react/mui";
7 |
8 | const defaultFooterStyles = {
9 | };
10 |
11 | class CustomFooter extends React.Component {
12 |
13 | handleRowChange = event => {
14 | this.props.changeRowsPerPage(event.target.value);
15 | };
16 |
17 | handlePageChange = (_, page) => {
18 | this.props.changePage(page);
19 | };
20 |
21 | render() {
22 | const { count, classes, textLabels, rowsPerPage, page } = this.props;
23 |
24 | const footerStyle = {
25 | display:'flex',
26 | justifyContent: 'flex-end',
27 | padding: '0px 24px 0px 24px'
28 | };
29 |
30 | return (
31 |
32 |
33 |
34 | Custom Option
35 |
36 | `${from}-${to} ${textLabels.displayRows} ${count}`}
43 | backIconButtonProps={{
44 | 'aria-label': textLabels.previous,
45 | }}
46 | nextIconButtonProps={{
47 | 'aria-label': textLabels.next,
48 | }}
49 | rowsPerPageOptions={[10,20,100]}
50 | onChangePage={this.handlePageChange}
51 | onChangeRowsPerPage={this.handleRowChange}
52 | />
53 |
54 |
55 |
56 | );
57 | }
58 |
59 | }
60 |
61 | export default withStyles(CustomFooter, defaultFooterStyles, { name: "CustomFooter" });
--------------------------------------------------------------------------------
/examples/customize-rows/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | See another example of how to use `customRowRender` at
3 | https://github.com/Skn0tt/mui-datatables-responsive-demo
4 | https://mui-datatables-responsive-demo.skn0tt.now.sh
5 | */
6 |
7 | import React from 'react';
8 | import ReactDOM from "react-dom";
9 | import MuiDataTable from "../../src/";
10 |
11 | function YourCustomRowComponent(props) {
12 | const { name, cardNumber, cvc, expiry } = props;
13 |
14 | return (
15 |
16 |
17 | {name}
18 |
19 |
20 | Number: {cardNumber}
21 | CVC: {cvc}
22 | expiry: {expiry}
23 |
24 |
25 | );
26 | }
27 |
28 | const creditCards = [
29 | {
30 | name: "Tom Tallis",
31 | cardNumber: "5500005555555559",
32 | cvc: "582",
33 | expiry: "02/24"
34 | },
35 | {
36 | name: "Rich Harris",
37 | cardNumber: "4444444444444448",
38 | cvc: "172",
39 | expiry: "03/22"
40 | },
41 | {
42 | name: "Moby Dixon",
43 | cardNumber: "3566003566003566",
44 | cvc: "230",
45 | expiry: "12/25"
46 | }
47 | ];
48 |
49 | function Example() {
50 | return (
51 | {
76 | const [ name, cardNumber, cvc, expiry ] = data;
77 |
78 | return (
79 |
80 |
81 |
87 |
88 |
89 | );
90 | },
91 | }}
92 | />
93 | );
94 | }
95 |
96 | export default Example;
97 |
--------------------------------------------------------------------------------
/examples/customize-search-render/CustomSearchRender.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Grow from '@mui/material/Grow';
3 | import TextField from '@mui/material/TextField';
4 | import IconButton from '@mui/material/IconButton';
5 | import ClearIcon from '@mui/icons-material/Clear';
6 | import { withStyles } from "tss-react/mui";
7 |
8 | const defaultSearchStyles = theme => ({
9 | main: {
10 | display: 'flex',
11 | flex: '1 0 auto',
12 | },
13 | searchText: {
14 | flex: '0.8 0',
15 | },
16 | clearIcon: {
17 | '&:hover': {
18 | color: theme.palette.error.main,
19 | },
20 | },
21 | });
22 |
23 | class CustomSearchRender extends React.Component {
24 | handleTextChange = event => {
25 | this.props.onSearch(event.target.value);
26 | };
27 |
28 | componentDidMount() {
29 | document.addEventListener('keydown', this.onKeyDown, false);
30 | }
31 |
32 | componentWillUnmount() {
33 | document.removeEventListener('keydown', this.onKeyDown, false);
34 | }
35 |
36 | onKeyDown = event => {
37 | if (event.keyCode === 27) {
38 | this.props.onHide();
39 | }
40 | };
41 |
42 | render() {
43 | const { classes, options, onHide, searchText } = this.props;
44 |
45 | return (
46 |
47 | (this.rootRef = el)}>
48 | (this.searchField = el)}
58 | />
59 |
60 |
61 |
62 |
63 |
64 | );
65 | }
66 | }
67 |
68 | export default withStyles(CustomSearchRender, defaultSearchStyles, { name: 'CustomSearchRender' });
69 |
--------------------------------------------------------------------------------
/examples/customize-search-render/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import MUIDataTable from '../../src/';
4 | import CustomSearchRender from './CustomSearchRender';
5 |
6 | class Example extends React.Component {
7 |
8 | render () {
9 |
10 | const columns = ['Name', 'Title', 'Location', 'Age', 'Salary'];
11 |
12 | let data = [
13 | ['Gabby George', 'Business Analyst', 'Minneapolis', 30, 100000],
14 | ['Aiden Lloyd', 'Business Consultant', 'Dallas', 55, 200000],
15 | ['Jaden Collins', 'Attorney', 'Santa Ana', 27, 500000],
16 | ['Franky Rees', 'Business Analyst', 'St. Petersburg', 22, 50000],
17 | ['Aaren Rose', 'Business Consultant', 'Toledo', 28, 75000],
18 | ['Blake Duncan', 'Business Management Analyst', 'San Diego', 65, 94000],
19 | ['Frankie Parry', 'Agency Legal Counsel', 'Jacksonville', 71, 210000],
20 | ['Lane Wilson', 'Commercial Specialist', 'Omaha', 19, 65000],
21 | ['Robin Duncan', 'Business Analyst', 'Los Angeles', 20, 77000],
22 | ['Mel Brooks', 'Business Consultant', 'Oklahoma City', 37, 135000],
23 | ['Harper White', 'Attorney', 'Pittsburgh', 52, 420000],
24 | ['Kris Humphrey', 'Agency Legal Counsel', 'Laredo', 30, 150000],
25 | ['Frankie Long', 'Industrial Analyst', 'Austin', 31, 170000],
26 | ['Brynn Robbins', 'Business Analyst', 'Norfolk', 22, 90000],
27 | ['Justice Mann', 'Business Consultant', 'Chicago', 24, 133000],
28 | ['Addison Navarro', 'Business Management Analyst', 'New York', 50, 295000],
29 | ['Jesse Welch', 'Agency Legal Counsel', 'Seattle', 28, 200000],
30 | ['Eli Mejia', 'Commercial Specialist', 'Long Beach', 65, 400000],
31 | ['Gene Leblanc', 'Industrial Analyst', 'Hartford', 34, 110000],
32 | ['Danny Leon', 'Computer Scientist', 'Newark', 60, 220000],
33 | ['Lane Lee', 'Corporate Counselor', 'Cincinnati', 52, 180000],
34 | ['Jesse Hall', 'Business Analyst', 'Baltimore', 44, 99000],
35 | ['Danni Hudson', 'Agency Legal Counsel', 'Tampa', 37, 90000],
36 | ['Terry Macdonald', 'Commercial Specialist', 'Miami', 39, 140000],
37 | ['Justice Mccarthy', 'Attorney', 'Tucson', 26, 330000],
38 | ['Silver Carey', 'Computer Scientist', 'Memphis', 47, 250000],
39 | ['Franky Miles', 'Industrial Analyst', 'Buffalo', 49, 190000],
40 | ['Glen Nixon', 'Corporate Counselor', 'Arlington', 44, 80000],
41 | ['Gabby Strickland', 'Business Process Consultant', 'Scottsdale', 26, 45000],
42 | ['Mason Ray', 'Computer Scientist', 'San Francisco', 39, 142000]
43 | ];
44 |
45 | const options = {
46 | filter: true,
47 | selectableRows: 'multiple',
48 | filterType: 'dropdown',
49 | responsive: 'vertical',
50 | rowsPerPage: 10,
51 | customSearchRender: (searchText, handleSearch, hideSearch, options) => {
52 | return (
53 |
59 | );
60 | }
61 | };
62 |
63 | return (
64 |
65 | );
66 |
67 | }
68 | }
69 |
70 | export default Example;
71 |
--------------------------------------------------------------------------------
/examples/customize-search/index.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import MUIDataTable from '../../src/';
4 |
5 | class Example extends React.Component {
6 | state = {
7 | searchText: 'Computer'
8 | };
9 |
10 | render() {
11 | const columns = [
12 | {
13 | name: 'Name',
14 | options: {
15 | filter: true,
16 | display: 'excluded',
17 | },
18 | },
19 | {
20 | label: 'Modified Title Label',
21 | name: 'Title',
22 | options: {
23 | filter: true,
24 | },
25 | },
26 | {
27 | name: 'Location',
28 | options: {
29 | filter: false,
30 | },
31 | },
32 | {
33 | name: 'Age',
34 | options: {
35 | filter: true,
36 | },
37 | },
38 | {
39 | name: 'Salary',
40 | options: {
41 | filter: true,
42 | sort: false,
43 | },
44 | },
45 | ];
46 |
47 | const data = [
48 | ['Gabby George', 'Business Analyst', 'Minneapolis', 30, '$100,000'],
49 | ['Aiden Lloyd', 'Business Consultant', 'Dallas', 55, '$200,000'],
50 | ['Jaden Collins', 'Attorney', 'Santa Ana', 27, '$500,000'],
51 | ['Franky Rees', 'Business Analyst', 'St. Petersburg', 22, '$50,000'],
52 | ['Aaren Rose', 'Business Consultant', 'Toledo', 28, '$75,000'],
53 | ['Blake Duncan', 'Business Management Analyst', 'San Diego', 65, '$94,000'],
54 | ['Frankie Parry', 'Agency Legal Counsel', 'Jacksonville', 71, '$210,000'],
55 | ['Lane Wilson', 'Commercial Specialist', 'Omaha', 19, '$65,000'],
56 | ['Robin Duncan', 'Business Analyst', 'Los Angeles', 20, '$77,000'],
57 | ['Mel Brooks', 'Business Consultant', 'Oklahoma City', 37, '$135,000'],
58 | ['Harper White', 'Attorney', 'Pittsburgh', 52, '$420,000'],
59 | ['Kris Humphrey', 'Agency Legal Counsel', 'Laredo', 30, '$150,000'],
60 | ['Frankie Long', 'Industrial Analyst', 'Austin', 31, '$170,000'],
61 | ['Brynn Robbins', 'Business Analyst', 'Norfolk', 22, '$90,000'],
62 | ['Justice Mann', 'Business Consultant', 'Chicago', 24, '$133,000'],
63 | ['Addison Navarro', 'Business Management Analyst', 'New York', 50, '$295,000'],
64 | ['Jesse Welch', 'Agency Legal Counsel', 'Seattle', 28, '$200,000'],
65 | ['Eli Mejia', 'Commercial Specialist', 'Long Beach', 65, '$400,000'],
66 | ['Gene Leblanc', 'Industrial Analyst', 'Hartford', 34, '$110,000'],
67 | ['Danny Leon', 'Computer Scientist', 'Newark', 60, '$220,000'],
68 | ['Lane Lee', 'Corporate Counselor', 'Cincinnati', 52, '$180,000'],
69 | ['Jesse Hall', 'Business Analyst', 'Baltimore', 44, '$99,000'],
70 | ['Danni Hudson', 'Agency Legal Counsel', 'Tampa', 37, '$90,000'],
71 | ['Terry Macdonald', 'Commercial Specialist', 'Miami', 39, '$140,000'],
72 | ['Justice Mccarthy', 'Attorney', 'Tucson', 26, '$330,000'],
73 | ['Silver Carey', 'Computer Scientist', 'Memphis', 47, '$250,000'],
74 | ['Franky Miles', 'Industrial Analyst', 'Buffalo', 49, '$190,000'],
75 | ['Glen Nixon', 'Corporate Counselor', 'Arlington', 44, '$80,000'],
76 | ['Gabby Strickland', 'Business Process Consultant', 'Scottsdale', 26, '$45,000'],
77 | ['Mason Ray', 'Computer Scientist', 'San Francisco', 39, '$142,000'],
78 | ];
79 |
80 | const options = {
81 | filter: true,
82 | filterType: 'dropdown',
83 | responsive: 'vertical',
84 | page: 0,
85 | searchText: this.state.searchText,
86 | searchProps: {
87 | onBlur: (e) => {
88 | console.log('onBlur!');
89 | },
90 | onKeyUp:(e) => {
91 | console.log('onKeyUp!');
92 | }
93 | },
94 | searchPlaceholder: 'Your Custom Search Placeholder',
95 | customSearch: (searchQuery, currentRow, columns) => {
96 | let isFound = false;
97 | currentRow.forEach(col => {
98 | if (col.toString().indexOf(searchQuery) >= 0) {
99 | isFound = true;
100 | }
101 | });
102 | return isFound;
103 | },
104 | };
105 |
106 | return (
107 |
108 | this.setState({ searchText: '' })}>Reset Search
109 |
110 |
111 | );
112 | }
113 | }
114 |
115 | export default Example;
116 |
--------------------------------------------------------------------------------
/examples/customize-sorting/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import MUIDataTable from "../../src/";
4 |
5 | class Example extends React.Component {
6 |
7 | render() {
8 |
9 | const columns = ["Name", "SurveyScore", "Date"];
10 |
11 | const data = [
12 | ["Gabby George", 3, "2018-07-06T23:58:59.000Z"],
13 | ["Aiden Lloyd", 10, "2018-07-06T23:58:53.000Z"],
14 | ["Jaden Collins", 3, "2018-07-06T23:55:51.000Z"],
15 | ["Franky Rees", 3, "2018-07-06T22:47:56.000Z"],
16 | ["Aaren Rose", 8, "2018-07-06T21:59:20.000Z"]
17 | ];
18 |
19 | const options = {
20 | filter: true,
21 | filterType: 'dropdown',
22 | responsive: 'vertical',
23 | customSort: (data, colIndex, order, meta) => {
24 | return data.sort((a, b) => {
25 | return (a.data[colIndex].length < b.data[colIndex].length ? -1: 1 ) * (order === 'desc' ? 1 : -1);
26 | });
27 | }
28 | };
29 |
30 | return (
31 |
32 | );
33 |
34 | }
35 | }
36 |
37 | export default Example;
38 |
--------------------------------------------------------------------------------
/examples/customize-toolbar-icons/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import MUIDataTable from "../../src/";
4 | import SearchIcon from '@mui/icons-material/YoutubeSearchedFor';
5 | import PrintIcon from '@mui/icons-material/Receipt';
6 | import DownloadIcon from '@mui/icons-material/GetApp';
7 | import ViewColumnIcon from '@mui/icons-material/DynamicFeed';
8 | import FilterIcon from '@mui/icons-material/GroupWork';
9 |
10 | class Example extends React.Component {
11 |
12 | render() {
13 |
14 | const columns = ["Name", "Title", "Location", "Age", "Salary"];
15 |
16 | let data = [
17 | ["Gabby George", "Business Analyst", "Minneapolis", 30, 100000],
18 | ["Aiden Lloyd", "Business Consultant", "Dallas", 55, 200000],
19 | ["Jaden Collins", "Attorney", "Santa Ana", 27, 500000],
20 | ["Franky Rees", "Business Analyst", "St. Petersburg", 22, 50000],
21 | ["Aaren Rose", "Business Consultant", "Toledo", 28, 75000],
22 | ["Blake Duncan", "Business Management Analyst", "San Diego", 65, 94000],
23 | ["Frankie Parry", "Agency Legal Counsel", "Jacksonville", 71, 210000],
24 | ["Lane Wilson", "Commercial Specialist", "Omaha", 19, 65000],
25 | ["Robin Duncan", "Business Analyst", "Los Angeles", 20, 77000],
26 | ["Mel Brooks", "Business Consultant", "Oklahoma City", 37, 135000],
27 | ["Harper White", "Attorney", "Pittsburgh", 52, 420000],
28 | ["Kris Humphrey", "Agency Legal Counsel", "Laredo", 30, 150000],
29 | ["Frankie Long", "Industrial Analyst", "Austin", 31, 170000],
30 | ["Brynn Robbins", "Business Analyst", "Norfolk", 22, 90000],
31 | ["Justice Mann", "Business Consultant", "Chicago", 24, 133000],
32 | ["Addison Navarro", "Business Management Analyst", "New York", 50, 295000],
33 | ["Jesse Welch", "Agency Legal Counsel", "Seattle", 28, 200000],
34 | ["Eli Mejia", "Commercial Specialist", "Long Beach", 65, 400000],
35 | ["Gene Leblanc", "Industrial Analyst", "Hartford", 34, 110000],
36 | ["Danny Leon", "Computer Scientist", "Newark", 60, 220000],
37 | ["Lane Lee", "Corporate Counselor", "Cincinnati", 52, 180000],
38 | ["Jesse Hall", "Business Analyst", "Baltimore", 44, 99000],
39 | ["Danni Hudson", "Agency Legal Counsel", "Tampa", 37, 90000],
40 | ["Terry Macdonald", "Commercial Specialist", "Miami", 39, 140000],
41 | ["Justice Mccarthy", "Attorney", "Tucson", 26, 330000],
42 | ["Silver Carey", "Computer Scientist", "Memphis", 47, 250000],
43 | ["Franky Miles", "Industrial Analyst", "Buffalo", 49, 190000],
44 | ["Glen Nixon", "Corporate Counselor", "Arlington", 44, 80000],
45 | ["Gabby Strickland", "Business Process Consultant", "Scottsdale", 26, 45000],
46 | ["Mason Ray", "Computer Scientist", "San Francisco", 39, 142000]
47 | ];
48 |
49 | const options = {
50 | filter: true,
51 | selectableRows: 'multiple',
52 | filterType: 'dropdown',
53 | responsive: 'vertical',
54 | rowsPerPage: 10,
55 | };
56 |
57 | const components = {
58 | icons: {
59 | SearchIcon,
60 | PrintIcon,
61 | DownloadIcon,
62 | ViewColumnIcon,
63 | FilterIcon,
64 | }
65 | };
66 |
67 | return (
68 |
69 | );
70 |
71 | }
72 | }
73 |
74 | export default Example;
75 |
--------------------------------------------------------------------------------
/examples/customize-toolbar/CustomToolbar.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import IconButton from "@mui/material/IconButton";
3 | import Tooltip from "@mui/material/Tooltip";
4 | import AddIcon from "@mui/icons-material/Add";
5 | import { withStyles } from "tss-react/mui";
6 | const defaultToolbarStyles = {
7 | iconButton: {
8 | },
9 | };
10 |
11 | class CustomToolbar extends React.Component {
12 |
13 | handleClick = () => {
14 | console.log("clicked on icon!");
15 | }
16 |
17 | render() {
18 | const { classes } = this.props;
19 |
20 | return (
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | );
29 | }
30 |
31 | }
32 |
33 | export default withStyles(CustomToolbar, defaultToolbarStyles, { name: "CustomToolbar" });
34 |
--------------------------------------------------------------------------------
/examples/customize-toolbar/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import MUIDataTable from "../../src/";
4 | import CustomToolbar from "./CustomToolbar";
5 |
6 | class Example extends React.Component {
7 |
8 | render() {
9 |
10 | const columns = ["Name", "Title", "Location", "Age", "Salary"];
11 |
12 | let data = [
13 | ["Gabby George", "Business Analyst", "Minneapolis", 30, 100000],
14 | ["Aiden Lloyd", "Business Consultant", "Dallas", 55, 200000],
15 | ["Jaden Collins", "Attorney", "Santa Ana", 27, 500000],
16 | ["Franky Rees", "Business Analyst", "St. Petersburg", 22, 50000],
17 | ["Aaren Rose", "Business Consultant", "Toledo", 28, 75000],
18 | ["Blake Duncan", "Business Management Analyst", "San Diego", 65, 94000],
19 | ["Frankie Parry", "Agency Legal Counsel", "Jacksonville", 71, 210000],
20 | ["Lane Wilson", "Commercial Specialist", "Omaha", 19, 65000],
21 | ["Robin Duncan", "Business Analyst", "Los Angeles", 20, 77000],
22 | ["Mel Brooks", "Business Consultant", "Oklahoma City", 37, 135000],
23 | ["Harper White", "Attorney", "Pittsburgh", 52, 420000],
24 | ["Kris Humphrey", "Agency Legal Counsel", "Laredo", 30, 150000],
25 | ["Frankie Long", "Industrial Analyst", "Austin", 31, 170000],
26 | ["Brynn Robbins", "Business Analyst", "Norfolk", 22, 90000],
27 | ["Justice Mann", "Business Consultant", "Chicago", 24, 133000],
28 | ["Addison Navarro", "Business Management Analyst", "New York", 50, 295000],
29 | ["Jesse Welch", "Agency Legal Counsel", "Seattle", 28, 200000],
30 | ["Eli Mejia", "Commercial Specialist", "Long Beach", 65, 400000],
31 | ["Gene Leblanc", "Industrial Analyst", "Hartford", 34, 110000],
32 | ["Danny Leon", "Computer Scientist", "Newark", 60, 220000],
33 | ["Lane Lee", "Corporate Counselor", "Cincinnati", 52, 180000],
34 | ["Jesse Hall", "Business Analyst", "Baltimore", 44, 99000],
35 | ["Danni Hudson", "Agency Legal Counsel", "Tampa", 37, 90000],
36 | ["Terry Macdonald", "Commercial Specialist", "Miami", 39, 140000],
37 | ["Justice Mccarthy", "Attorney", "Tucson", 26, 330000],
38 | ["Silver Carey", "Computer Scientist", "Memphis", 47, 250000],
39 | ["Franky Miles", "Industrial Analyst", "Buffalo", 49, 190000],
40 | ["Glen Nixon", "Corporate Counselor", "Arlington", 44, 80000],
41 | ["Gabby Strickland", "Business Process Consultant", "Scottsdale", 26, 45000],
42 | ["Mason Ray", "Computer Scientist", "San Francisco", 39, 142000]
43 | ];
44 |
45 | const options = {
46 | filter: true,
47 | selectableRows: 'multiple',
48 | filterType: 'dropdown',
49 | responsive: 'vertical',
50 | rowsPerPage: 10,
51 | customToolbar: () => {
52 | return (
53 |
54 | );
55 | }
56 | };
57 |
58 | return (
59 |
60 | );
61 |
62 | }
63 | }
64 |
65 | export default Example;
66 |
--------------------------------------------------------------------------------
/examples/customize-toolbarselect/CustomToolbarSelect.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import IconButton from "@mui/material/IconButton";
3 | import Tooltip from "@mui/material/Tooltip";
4 | import CompareArrowsIcon from "@mui/icons-material/CompareArrows";
5 | import IndeterminateCheckBoxIcon from "@mui/icons-material/IndeterminateCheckBox";
6 | import BlockIcon from "@mui/icons-material/Block";
7 | import { withStyles } from "tss-react/mui";
8 |
9 | const defaultToolbarSelectStyles = {
10 | iconButton: {
11 | },
12 | iconContainer: {
13 | marginRight: "24px",
14 | },
15 | inverseIcon: {
16 | transform: "rotate(90deg)",
17 | },
18 | };
19 |
20 | class CustomToolbarSelect extends React.Component {
21 | handleClickInverseSelection = () => {
22 | const nextSelectedRows = this.props.displayData.reduce((nextSelectedRows, _, index) => {
23 | if (!this.props.selectedRows.data.find(selectedRow => selectedRow.index === index)) {
24 | nextSelectedRows.push(index);
25 | }
26 |
27 | return nextSelectedRows;
28 | }, []);
29 |
30 | this.props.setSelectedRows(nextSelectedRows);
31 | };
32 |
33 | handleClickDeselectAll = () => {
34 | this.props.setSelectedRows([]);
35 | };
36 |
37 | handleClickBlockSelected = () => {
38 | console.log(`block users with dataIndexes: ${this.props.selectedRows.data.map(row => row.dataIndex)}`);
39 | };
40 |
41 | render() {
42 | const { classes } = this.props;
43 |
44 | return (
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | );
63 | }
64 | }
65 |
66 | export default withStyles(CustomToolbarSelect, defaultToolbarSelectStyles, { name: "CustomToolbarSelect" });
67 |
--------------------------------------------------------------------------------
/examples/customize-toolbarselect/index.js:
--------------------------------------------------------------------------------
1 | import React, {useState} from "react";
2 | import ReactDOM from "react-dom";
3 | import MUIDataTable from "../../src/";
4 | import CustomToolbarSelect from "./CustomToolbarSelect";
5 | import InputLabel from '@mui/material/InputLabel';
6 |
7 | import MenuItem from '@mui/material/MenuItem';
8 | import FormHelperText from '@mui/material/FormHelperText';
9 | import FormControl from '@mui/material/FormControl';
10 | import Select from '@mui/material/Select';
11 |
12 | function Example() {
13 |
14 | const [stp, setStp] = useState("replace");
15 |
16 | const columns = ["Name", "Title", "Location", "Age", "Salary"];
17 |
18 | let data = [
19 | ["Gabby George", "Business Analyst", "Minneapolis", 30, 100000],
20 | ["Aiden Lloyd", "Business Consultant", "Dallas", 55, 200000],
21 | ["Jaden Collins", "Attorney", "Santa Ana", 27, 500000],
22 | ["Franky Rees", "Business Analyst", "St. Petersburg", 22, 50000],
23 | ["Aaren Rose", "Business Consultant", "Toledo", 28, 75000],
24 | ["Blake Duncan", "Business Management Analyst", "San Diego", 65, 94000],
25 | ["Frankie Parry", "Agency Legal Counsel", "Jacksonville", 71, 210000],
26 | ["Lane Wilson", "Commercial Specialist", "Omaha", 19, 65000],
27 | ["Robin Duncan", "Business Analyst", "Los Angeles", 20, 77000],
28 | ["Mel Brooks", "Business Consultant", "Oklahoma City", 37, 135000],
29 | ["Harper White", "Attorney", "Pittsburgh", 52, 420000],
30 | ["Kris Humphrey", "Agency Legal Counsel", "Laredo", 30, 150000],
31 | ["Frankie Long", "Industrial Analyst", "Austin", 31, 170000],
32 | ["Brynn Robbins", "Business Analyst", "Norfolk", 22, 90000],
33 | ["Justice Mann", "Business Consultant", "Chicago", 24, 133000],
34 | ["Addison Navarro", "Business Management Analyst", "New York", 50, 295000],
35 | ["Jesse Welch", "Agency Legal Counsel", "Seattle", 28, 200000],
36 | ["Eli Mejia", "Commercial Specialist", "Long Beach", 65, 400000],
37 | ["Gene Leblanc", "Industrial Analyst", "Hartford", 34, 110000],
38 | ["Danny Leon", "Computer Scientist", "Newark", 60, 220000],
39 | ["Lane Lee", "Corporate Counselor", "Cincinnati", 52, 180000],
40 | ["Jesse Hall", "Business Analyst", "Baltimore", 44, 99000],
41 | ["Danni Hudson", "Agency Legal Counsel", "Tampa", 37, 90000],
42 | ["Terry Macdonald", "Commercial Specialist", "Miami", 39, 140000],
43 | ["Justice Mccarthy", "Attorney", "Tucson", 26, 330000],
44 | ["Silver Carey", "Computer Scientist", "Memphis", 47, 250000],
45 | ["Franky Miles", "Industrial Analyst", "Buffalo", 49, 190000],
46 | ["Glen Nixon", "Corporate Counselor", "Arlington", 44, 80000],
47 | ["Gabby Strickland", "Business Process Consultant", "Scottsdale", 26, 45000],
48 | ["Mason Ray", "Computer Scientist", "San Francisco", 39, 142000],
49 | ];
50 |
51 | const options = {
52 | filter: true,
53 | selectableRows: 'multiple',
54 | filterType: "dropdown",
55 | responsive: "vertical",
56 | rowsPerPage: 10,
57 | selectToolbarPlacement: stp,
58 | customToolbarSelect: (selectedRows, displayData, setSelectedRows) => (
59 |
60 | ),
61 | };
62 |
63 | return (
64 | <>
65 |
66 | Select Toolbar Placement
67 | setStp(e.target.value)}
73 | >
74 | none
75 | replace
76 | above
77 |
78 |
79 |
80 | >
81 | );
82 | }
83 |
84 | export default Example;
85 |
--------------------------------------------------------------------------------
/examples/data-as-objects/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import MUIDataTable from "../../src/";
4 |
5 | class Example extends React.Component {
6 |
7 | state = {
8 | counter: 0,
9 | data: [
10 | { name: "Gabby George", title: "Business Analyst", location: "Minneapolis", age: 30, salary: "$100,000", phone: { home: '867-5309', cell: '123-4567' } },
11 | { name: "Aiden Lloyd", title: "Business Consultant", location: "Dallas", age: 55, salary: "$200,000", phone: { home: '867-5310', cell: '123-4568' } },
12 | { name: "Jaden Collins", title: "Attorney", location: "Santa Ana", age: 27, salary: "$500,000", phone: { home: '867-5311', cell: '123-4569' } },
13 | { name: "Franky Rees", title: "Business Analyst", location: "St. Petersburg", age: 22, salary: "$50,000", phone: { home: '867-5312', cell: '123-4569' } }
14 | ]
15 | }
16 |
17 | rerender = () => {
18 | this.setState((prevState, props) => ({
19 | counter: prevState.counter + 1
20 | }));
21 | }
22 |
23 | render() {
24 |
25 | const columns = [
26 | {
27 | name: "name",
28 | label: "Name",
29 | options: {
30 | filter: true,
31 | display: 'excluded',
32 | }
33 | },
34 | {
35 | name: "title",
36 | label: "Modified Title Label",
37 | options: {
38 | filter: true,
39 | }
40 | },
41 | {
42 | name: "location",
43 | label: "Location",
44 | options: {
45 | filter: false,
46 | }
47 | },
48 | {
49 | name: "age",
50 | Label: "Age",
51 | options: {
52 | filter: true,
53 | }
54 | },
55 | {
56 | name: "salary",
57 | label: "Salary",
58 | options: {
59 | filter: true,
60 | sort: false
61 | }
62 | },
63 | {
64 | name: "phone.home",
65 | label: "Home Phone",
66 | },
67 | {
68 | name: "phone.cell",
69 | label: "Cell Phone #",
70 | },
71 | {
72 | name: "phone2.home",
73 | label: "Not An Attribute",
74 | }
75 | ];
76 |
77 | const options = {
78 | filter: true,
79 | filterType: 'dropdown',
80 | responsive: 'vertical',
81 | enableNestedDataAccess: '.', // allows nested data separated by "." (see column names and the data structure above)
82 | };
83 |
84 | return (
85 |
86 | Re-render - {this.state.counter}
87 |
88 |
89 | );
90 |
91 | }
92 | }
93 |
94 | export default Example;
--------------------------------------------------------------------------------
/examples/examples.js:
--------------------------------------------------------------------------------
1 | import ArrayValueColumns from './array-value-columns';
2 | import ColumnFilters from './column-filters';
3 | import ColumnOptionsUpdate from './column-options-update';
4 | import ColumnSort from "./column-sort";
5 | import Component from './component';
6 | import CSVExport from './csv-export';
7 | import CustomActionColumns from './custom-action-columns';
8 | import CustomizeColumns from './customize-columns';
9 | import CustomizeFilter from './customize-filter';
10 | import CustomizeFooter from './customize-footer';
11 | import CustomizeRows from './customize-rows';
12 | import CustomizeSearch from './customize-search';
13 | import CustomizeSearchRender from './customize-search-render';
14 | import CustomizeSorting from './customize-sorting';
15 | import CustomizeStyling from './customize-styling';
16 | import CustomizeToolbar from './customize-toolbar';
17 | import CustomizeToolbarIcons from './customize-toolbar-icons';
18 | import CustomizeToolbarSelect from './customize-toolbarselect';
19 | import DataAsObjects from './data-as-objects';
20 | import DraggableColumns from './draggable-columns';
21 | import ExpandableRows from './expandable-rows';
22 | import FixedHeader from './fixed-header';
23 | import HideColumnsPrint from './hide-columns-print';
24 | import OnDownload from './on-download';
25 | import OnTableInit from './on-table-init';
26 | import ResizableColumns from './resizable-columns';
27 | import SelectableRows from './selectable-rows';
28 | import ServerSideFilters from './serverside-filters';
29 | import ServerSidePagination from './serverside-pagination';
30 | import ServerSideSorting from './serverside-sorting';
31 | import Simple from './simple';
32 | import SimpleNoToolbar from './simple-no-toolbar';
33 | import TextLocalization from './text-localization';
34 | import CustomComponents from './custom-components';
35 | import InfiniteScrolling from './infinite-scrolling';
36 | import Themes from './themes';
37 | import LargeDataSet from './large-data-set';
38 |
39 | /**
40 | * Here you can add any extra examples with the Card label as the key, and the component to render as the value
41 | */
42 | export default {
43 | 'Array Value Columns': ArrayValueColumns,
44 | 'Column Filters': ColumnFilters,
45 | 'Column Option Update': ColumnOptionsUpdate,
46 | 'Column Sort': ColumnSort,
47 | Component: Component,
48 | 'CSV Export': CSVExport,
49 | 'Custom Action Columns': CustomActionColumns,
50 | 'Customize Columns': CustomizeColumns,
51 | 'Customize Filter': CustomizeFilter,
52 | 'Customize Footer': CustomizeFooter,
53 | 'Customize Rows': CustomizeRows,
54 | 'Customize Search': CustomizeSearch,
55 | 'Customize Search Render': CustomizeSearchRender,
56 | 'Customize Sorting': CustomizeSorting,
57 | 'Customize Styling': CustomizeStyling,
58 | 'Customize Toolbar': CustomizeToolbar,
59 | 'Customize Toolbar Icons': CustomizeToolbarIcons,
60 | 'Customize Toolbar Select': CustomizeToolbarSelect,
61 | 'Data As Objects': DataAsObjects,
62 | 'Draggable Columns': DraggableColumns,
63 | 'Expandable Rows': ExpandableRows,
64 | 'Fixed Header': FixedHeader,
65 | 'Hide Columns Print': HideColumnsPrint,
66 | 'Infinite Scrolling': InfiniteScrolling,
67 | 'Large Data Set': LargeDataSet,
68 | 'OnDownload': OnDownload,
69 | 'OnTableInit': OnTableInit,
70 | 'Resizable Columns': ResizableColumns,
71 | 'Selectable Rows': SelectableRows,
72 | 'ServerSide Filters': ServerSideFilters,
73 | 'ServerSide Pagination': ServerSidePagination,
74 | 'ServerSide Sorting': ServerSideSorting,
75 | 'Simple': Simple,
76 | 'Simple No Toolbar': SimpleNoToolbar,
77 | 'Text Localization': TextLocalization,
78 | 'Custom Components': CustomComponents,
79 | Themes: Themes,
80 | };
81 |
--------------------------------------------------------------------------------
/examples/hide-columns-print/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import MUIDataTable from "../../src/";
4 |
5 | class Example extends React.Component {
6 |
7 | render() {
8 |
9 | const columns = [
10 | {
11 | name: "Name",
12 | options: {
13 | filter: true,
14 | display: 'excluded',
15 | }
16 | },
17 | {
18 | label: "Modified Title Label",
19 | name: "Title",
20 | options: {
21 | filter: true,
22 | }
23 | },
24 | {
25 | name: "Location",
26 | options: {
27 | print: false,
28 | filter: false,
29 | }
30 | },
31 | {
32 | name: "Age",
33 | options: {
34 | filter: true,
35 | print: false,
36 | }
37 | },
38 | {
39 | name: "Salary",
40 | options: {
41 | filter: true,
42 | sort: false
43 | }
44 | }
45 | ];
46 |
47 |
48 | const data = [
49 | ["Gabby George", "Business Analyst", "Minneapolis", 30, "$100,000"],
50 | ["Aiden Lloyd", "Business Consultant", "Dallas", 55, "$200,000"],
51 | ["Jaden Collins", "Attorney", "Santa Ana", 27, "$500,000"],
52 | ["Franky Rees", "Business Analyst", "St. Petersburg", 22, "$50,000"],
53 | ["Aaren Rose", "Business Consultant", "Toledo", 28, "$75,000"],
54 | ["Blake Duncan", "Business Management Analyst", "San Diego", 65, "$94,000"],
55 | ["Frankie Parry", "Agency Legal Counsel", "Jacksonville", 71, "$210,000"],
56 | ["Lane Wilson", "Commercial Specialist", "Omaha", 19, "$65,000"],
57 | ["Robin Duncan", "Business Analyst", "Los Angeles", 20, "$77,000"],
58 | ["Mel Brooks", "Business Consultant", "Oklahoma City", 37, "$135,000"],
59 | ["Harper White", "Attorney", "Pittsburgh", 52, "$420,000"],
60 | ["Kris Humphrey", "Agency Legal Counsel", "Laredo", 30, "$150,000"],
61 | ["Frankie Long", "Industrial Analyst", "Austin", 31, "$170,000"],
62 | ["Brynn Robbins", "Business Analyst", "Norfolk", 22, "$90,000"],
63 | ["Justice Mann", "Business Consultant", "Chicago", 24, "$133,000"],
64 | ["Addison Navarro", "Business Management Analyst", "New York", 50, "$295,000"],
65 | ["Jesse Welch", "Agency Legal Counsel", "Seattle", 28, "$200,000"],
66 | ["Eli Mejia", "Commercial Specialist", "Long Beach", 65, "$400,000"],
67 | ["Gene Leblanc", "Industrial Analyst", "Hartford", 34, "$110,000"],
68 | ["Danny Leon", "Computer Scientist", "Newark", 60, "$220,000"],
69 | ["Lane Lee", "Corporate Counselor", "Cincinnati", 52, "$180,000"],
70 | ["Jesse Hall", "Business Analyst", "Baltimore", 44, "$99,000"],
71 | ["Danni Hudson", "Agency Legal Counsel", "Tampa", 37, "$90,000"],
72 | ["Terry Macdonald", "Commercial Specialist", "Miami", 39, "$140,000"],
73 | ["Justice Mccarthy", "Attorney", "Tucson", 26, "$330,000"],
74 | ["Silver Carey", "Computer Scientist", "Memphis", 47, "$250,000" ],
75 | ["Franky Miles", "Industrial Analyst", "Buffalo", 49, "$190,000"],
76 | ["Glen Nixon", "Corporate Counselor", "Arlington", 44, "$80,000"],
77 | ["Gabby Strickland", "Business Process Consultant", "Scottsdale", 26, "$45,000"],
78 | ["Mason Ray", "Computer Scientist", "San Francisco", 39, "$142,000"]
79 | ];
80 |
81 | const options = {
82 | filter: true,
83 | filterType: 'dropdown',
84 | responsive: 'standard',
85 | };
86 |
87 | return (
88 |
89 | );
90 |
91 | }
92 | }
93 |
94 | export default Example;
95 |
--------------------------------------------------------------------------------
/examples/infinite-scrolling/index.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, Component } from "react";
2 | import { Waypoint } from "react-waypoint";
3 | import PropTypes from "prop-types";
4 | import MUIDataTable from "../../src/";
5 | import { createTheme } from "@mui/material/styles";
6 | import {
7 | Table,
8 | TableBody,
9 | TableCell,
10 | TableHead,
11 | TableRow,
12 | Paper
13 | } from "@mui/material";
14 | import { withStyles } from "tss-react/mui";
15 |
16 | const styles = theme => ({
17 | root: {
18 | width: "100%",
19 | overflowX: "auto",
20 | height: 300,
21 | flexGrow: 1
22 | },
23 | head: {
24 | backgroundColor: theme.palette.primary.main,
25 | color: "#fff",
26 | position: "sticky",
27 | fontSize: ".6rem",
28 | top: 0
29 | },
30 | table: {
31 | minWidth: 700,
32 | height: 200
33 | },
34 | tableCell: {
35 | fontSize: ".6rem"
36 | }
37 | });
38 |
39 | class MessageManager extends Component {
40 | constructor(props) {
41 | super(props);
42 | this.props = props;
43 |
44 | this.state = {
45 | filteredMessages: []
46 | };
47 | }
48 |
49 | componentDidMount() {
50 | this.getMessages(0);
51 | }
52 |
53 | columns = [
54 | {
55 | name: "Id",
56 | options: {
57 | filter: false,
58 | sort: false,
59 | customBodyRenderLite: (dataIndex, rowIndex) => {
60 | const { filteredMessages } = this.state;
61 | let value = filteredMessages[dataIndex][0];
62 |
63 | if (rowIndex === filteredMessages.length - 10) {
64 | return (
65 |
66 | {
68 | console.log("WAYPOINT REACHED");
69 | const newData = this.buildTestData(
70 | 30,
71 | filteredMessages.length
72 | );
73 |
74 | this.setState({
75 | filteredMessages: [...filteredMessages, ...newData]
76 | });
77 | }}
78 | />
79 | {value}*
80 |
81 | );
82 | } else {
83 | return {value} ;
84 | }
85 | }
86 | }
87 | },
88 | {
89 | name: "Message",
90 | options:{
91 | sort: false,
92 | }
93 | },
94 | {
95 | name: "Requester",
96 | options:{
97 | sort: false,
98 | }
99 | }
100 | ];
101 |
102 | options = {
103 | filter: false,
104 | fixedHeader: true,
105 | filterType: "dropdown",
106 | responsive: "standard",
107 | selectableRows: "none",
108 | pagination: false,
109 | tableBodyHeight:'500px',
110 | onRowClick(rowNode) {
111 | console.log(rowNode);
112 | }
113 | };
114 |
115 | /*eslint-disable */
116 | buildTestData(count, startingIndex) {
117 | const data = [
118 | ["Template 1", "Requester Jerry"],
119 | ["Template 2", "Test user 1"],
120 | ["Order66", "Test user 2"],
121 | ["Live Message", "Another Person"],
122 | ["Future Message", "John Doe"],
123 | ["Expired Message", "Jane Doe"],
124 | ["Retired Message", "Some Guy"]
125 | ];
126 |
127 | function createData(id, message, requester) {
128 | return [id, message, requester];
129 | }
130 |
131 | const rows = [];
132 |
133 | for (let i = 0; i < count; i += 1) {
134 | const randomSelection = data[Math.floor(Math.random() * data.length)];
135 | const id = i + 1 + startingIndex;
136 | rows.push(createData(id, ...randomSelection));
137 | }
138 | return rows;
139 | }
140 | /* eslint-enable */
141 |
142 | getMessages(pageNum) {
143 | const THIRTYROWS = 30;
144 | const messages = this.buildTestData(THIRTYROWS, 0);
145 | this.setState({
146 | filteredMessages: messages
147 | });
148 | }
149 |
150 | getMuiTheme = () =>
151 | createTheme({
152 | typography: {
153 | useNextVariants: true
154 | },
155 | overrides: {
156 | MUIDataTable: {
157 | root: {}
158 | },
159 | MUIDataTableBodyRow: {
160 | root: {
161 | "&:nth-child(odd)": {
162 | backgroundColor: "#f6f6f6"
163 | }
164 | }
165 | },
166 | MUIDataTableBodyCell: {}
167 | }
168 | });
169 |
170 | // eslint-disable-next-line max-lines-per-function
171 | render() {
172 | const { classes } = this.props;
173 | const { filteredMessages } = this.state;
174 | return (
175 |
176 |
181 |
182 | );
183 | }
184 | }
185 | MessageManager.propTypes = {
186 | classes: PropTypes.object.isRequired
187 | };
188 |
189 | export default withStyles(MessageManager, styles);
--------------------------------------------------------------------------------
/examples/large-data-set/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import MUIDataTable from "../../src/";
4 | import { debounceSearchRender } from "../../src/";
5 | import { Button } from '@mui/material';
6 |
7 | class Example extends React.Component {
8 |
9 | constructor(props) {
10 | super(props);
11 |
12 | const rand = (size) => {
13 | return Math.floor( Math.random() * size );
14 | };
15 |
16 | const firstNames = ['Adam', 'Jack', 'Edward', 'Donna', 'Sarah', 'Suzie', 'Sam', 'RJ', 'Henry', 'Ryan', 'Ricky', 'James'];
17 | const lastNames = ['Ronson', 'Johnson', 'Jackson', 'Campo', 'Edwards', 'Brown', 'Green', 'White', 'Simmons', 'Gates', 'Jobs'];
18 | const titles = ['Owner', 'Unemployed', 'Burger Flipper', 'Coder', 'Business Analyst', 'Attorney', 'Consultant', 'Singer','Painter'];
19 | const locations = ['New York', 'El Paso', 'DC', 'Dallas', 'Santa Ana','St. Petersburg', 'London','Paris'];
20 | const salarys = ['$100,000', '$50,000', '$75,000', '$80,000'];
21 | var data = [];
22 | var name = '';
23 |
24 | for (var ii = 0; ii < 5000; ii++) {
25 | name = firstNames[ rand(firstNames.length)] + ' ' + lastNames[ rand(lastNames.length) ];
26 | data.push({
27 | name: name,
28 | title: titles[ rand(titles.length)],
29 | location: locations[ rand(locations.length) ],
30 | salary: salarys[ rand(salarys.length )],
31 | phone: '555-5555',
32 | email: name.replace(/ /g, '_').toLowerCase() + '@example.com',
33 | });
34 | }
35 |
36 | this.state = {
37 | data: data
38 | };
39 | console.time('Render Time');
40 | }
41 |
42 | componentDidMount() {
43 | console.timeEnd('Render Time');
44 | }
45 |
46 | render() {
47 |
48 | const columns = [
49 | {
50 | name: "name",
51 | label: "Name",
52 | options: {
53 | filter: true,
54 | customBodyRenderLite: (dataIndex) => {
55 | let val = this.state.data[dataIndex].name;
56 | return val;
57 | }
58 | }
59 | },
60 | {
61 | name: "title",
62 | label: "Modified Title Label",
63 | options: {
64 | filter: true,
65 | customBodyRenderLite: (dataIndex) => {
66 | let val = this.state.data[dataIndex].title;
67 | return val;
68 | }
69 | }
70 | },
71 | {
72 | name: "location",
73 | label: "Location",
74 | options: {
75 | filter: false,
76 | }
77 | },
78 | {
79 | name: "age",
80 | label: "Age",
81 | options: {
82 | filter: true,
83 | }
84 | },
85 | {
86 | name: "salary",
87 | label: "Salary",
88 | options: {
89 | filter: true,
90 | sort: false,
91 | customBodyRenderLite: (dataIndex) => {
92 | let val = this.state.data[dataIndex].salary;
93 | return val;
94 | }
95 | }
96 | },
97 | {
98 | name: "phone",
99 | label: "Phone",
100 | options: {
101 | filter: true,
102 | sort: false,
103 | customBodyRenderLite: (dataIndex) => {
104 | let val = this.state.data[dataIndex].phone;
105 | return val;
106 | }
107 | }
108 | },
109 | {
110 | name: "email",
111 | label: "E-mail",
112 | options: {
113 | filter: true,
114 | sort: false,
115 | customBodyRenderLite: (dataIndex) => {
116 | let val = this.state.data[dataIndex].email;
117 | return val;
118 | }
119 | }
120 | }
121 | ];
122 |
123 | const options = {
124 | rowsPerPage: 100,
125 | rowsPerPageOptions: [10, 100, 250, 500, 1000],
126 | filter: true,
127 | filterType: 'dropdown',
128 | responsive: 'vertical',
129 | tableBodyHeight:'500px',
130 | customSearchRender: debounceSearchRender(500),
131 | jumpToPage: true,
132 |
133 | // These next two options allow you to make it so filters need to be confirmed.
134 | confirmFilters: true,
135 |
136 | // Calling the applyNewFilters parameter applies the selected filters to the table
137 | customFilterDialogFooter: (currentFilterList, applyNewFilters) => {
138 | return (
139 |
140 | Apply Filters
141 |
142 | );
143 | }
144 | };
145 |
146 | return (
147 |
148 | );
149 |
150 | }
151 | }
152 |
153 | export default Example;
154 |
--------------------------------------------------------------------------------
/examples/on-download/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import MUIDataTable from '../../src';
4 |
5 | class Example extends React.Component {
6 | render() {
7 | const columns = [
8 | {
9 | name: 'Name',
10 | options: {
11 | filter: true,
12 | display: 'excluded',
13 | },
14 | },
15 | {
16 | label: 'Modified Title Label',
17 | name: 'Title',
18 | options: {
19 | filter: true,
20 | },
21 | },
22 | {
23 | name: 'Location',
24 | options: {
25 | print: false,
26 | filter: false,
27 | },
28 | },
29 | {
30 | name: 'Age',
31 | options: {
32 | filter: true,
33 | print: false,
34 | },
35 | },
36 | {
37 | name: 'Salary',
38 | options: {
39 | filter: true,
40 | sort: false,
41 | },
42 | },
43 | ];
44 |
45 | const data = [
46 | ['Gabby George', 'Business Analyst', 'Minneapolis', 30, '$100,000'],
47 | ['Aiden Lloyd', 'Business Consultant', 'Dallas', 55, '$200,000'],
48 | ['Jaden Collins', 'Attorney', 'Santa Ana', 27, '$500,000'],
49 | ['Franky Rees', 'Business Analyst', 'St. Petersburg', 22, '$50,000'],
50 | ['Aaren Rose', 'Business Consultant', 'Toledo', 28, '$75,000'],
51 | ['Blake Duncan', 'Business Management Analyst', 'San Diego', 65, '$94,000'],
52 | ['Frankie Parry', 'Agency Legal Counsel', 'Jacksonville', 71, '$210,000'],
53 | ['Lane Wilson', 'Commercial Specialist', 'Omaha', 19, '$65,000'],
54 | ['Robin Duncan', 'Business Analyst', 'Los Angeles', 20, '$77,000'],
55 | ['Mel Brooks', 'Business Consultant', 'Oklahoma City', 37, '$135,000'],
56 | ['Harper White', 'Attorney', 'Pittsburgh', 52, '$420,000'],
57 | ['Kris Humphrey', 'Agency Legal Counsel', 'Laredo', 30, '$150,000'],
58 | ['Frankie Long', 'Industrial Analyst', 'Austin', 31, '$170,000'],
59 | ['Brynn Robbins', 'Business Analyst', 'Norfolk', 22, '$90,000'],
60 | ['Justice Mann', 'Business Consultant', 'Chicago', 24, '$133,000'],
61 | ['Addison Navarro', 'Business Management Analyst', 'New York', 50, '$295,000'],
62 | ['Jesse Welch', 'Agency Legal Counsel', 'Seattle', 28, '$200,000'],
63 | ['Eli Mejia', 'Commercial Specialist', 'Long Beach', 65, '$400,000'],
64 | ['Gene Leblanc', 'Industrial Analyst', 'Hartford', 34, '$110,000'],
65 | ['Danny Leon', 'Computer Scientist', 'Newark', 60, '$220,000'],
66 | ['Lane Lee', 'Corporate Counselor', 'Cincinnati', 52, '$180,000'],
67 | ['Jesse Hall', 'Business Analyst', 'Baltimore', 44, '$99,000'],
68 | ['Danni Hudson', 'Agency Legal Counsel', 'Tampa', 37, '$90,000'],
69 | ['Terry Macdonald', 'Commercial Specialist', 'Miami', 39, '$140,000'],
70 | ['Justice Mccarthy', 'Attorney', 'Tucson', 26, '$330,000'],
71 | ['Silver Carey', 'Computer Scientist', 'Memphis', 47, '$250,000'],
72 | ['Franky Miles', 'Industrial Analyst', 'Buffalo', 49, '$190,000'],
73 | ['Glen Nixon', 'Corporate Counselor', 'Arlington', 44, '$80,000'],
74 | ['Gabby Strickland', 'Business Process Consultant', 'Scottsdale', 26, '$45,000'],
75 | ['Mason Ray', 'Computer Scientist', 'San Francisco', 39, '$142,000'],
76 | ];
77 |
78 | // Data for building a custom head and body with the onDownload option
79 | const headerNames = [
80 | {
81 | name: 'Given name',
82 | download: true,
83 | },
84 | {
85 | name: 'Role',
86 | download: true,
87 | },
88 | {
89 | name: 'City',
90 | download: true,
91 | },
92 | {
93 | name: 'Years',
94 | download: true,
95 | },
96 | {
97 | name: 'Dough',
98 | download: true,
99 | },
100 | ];
101 | const footerNames = ['Full Name', 'Job', 'Whereabouts', 'Age', 'Allowance'];
102 |
103 | const options = {
104 | filter: true,
105 | filterType: 'dropdown',
106 | responsive: 'standard',
107 | onDownload: (buildHead, buildBody, columns, data) =>
108 | buildHead(headerNames) +
109 | buildBody(
110 | data.concat({
111 | index: data.length,
112 | data: footerNames,
113 | }),
114 | ),
115 | };
116 |
117 | return ;
118 | }
119 | }
120 |
121 | export default Example;
122 |
--------------------------------------------------------------------------------
/examples/on-table-init/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import MUIDataTable from '../../src';
4 |
5 | class Example extends React.Component {
6 | constructor(props) {
7 | super(props);
8 |
9 | this.state = {
10 | table: {}
11 | };
12 | this.handleTableInit = this.handleTableInit.bind(this);
13 | this.handleTableChange = this.handleTableChange.bind(this);
14 | }
15 |
16 | /** onTableInit gives access to initial MuiDataTable state
17 | * if the application needs access to internal state prior to
18 | * the user performing a table mutation (sort, filter, etc.)
19 | * that triggers onTableChange
20 | */
21 | handleTableInit = (action, tableState) => {
22 | console.log('handleTableInit: ', tableState);
23 | this.setState({ table: tableState });
24 | };
25 |
26 | handleTableChange = (action, tableState) => {
27 | console.log('handleTableChange: ', tableState);
28 | this.setState({ table: tableState });
29 | };
30 |
31 | columns = ['Name', 'Title', 'Location', 'Age', 'Salary'];
32 |
33 | data = [
34 | ['Gabby George', 'Business Analyst', 'Minneapolis', 30, 100000],
35 | ['Aiden Lloyd', 'Business Consultant', 'Dallas', 55, 200000],
36 | ['Jaden Collins', 'Attorney', 'Santa Ana', 27, 500000],
37 | ['Franky Rees', 'Business Analyst', 'St. Petersburg', 22, 50000],
38 | ['Aaren Rose', 'Business Consultant', 'Toledo', 28, 75000],
39 | ['Blake Duncan', 'Business Management Analyst', 'San Diego', 65, 94000],
40 | ['Frankie Parry', 'Agency Legal Counsel', 'Jacksonville', 71, 210000],
41 | ['Lane Wilson', 'Commercial Specialist', 'Omaha', 19, 65000],
42 | ['Robin Duncan', 'Business Analyst', 'Los Angeles', 20, 77000],
43 | ['Mel Brooks', 'Business Consultant', 'Oklahoma City', 37, 135000],
44 | ['Harper White', 'Attorney', 'Pittsburgh', 52, 420000],
45 | ['Kris Humphrey', 'Agency Legal Counsel', 'Laredo', 30, 150000],
46 | ['Frankie Long', 'Industrial Analyst', 'Austin', 31, 170000],
47 | ['Brynn Robbins', 'Business Analyst', 'Norfolk', 22, 90000],
48 | ['Justice Mann', 'Business Consultant', 'Chicago', 24, 133000],
49 | ['Addison Navarro', 'Business Management Analyst', 'New York', 50, 295000],
50 | ['Jesse Welch', 'Agency Legal Counsel', 'Seattle', 28, 200000],
51 | ['Eli Mejia', 'Commercial Specialist', 'Long Beach', 65, 400000],
52 | ['Gene Leblanc', 'Industrial Analyst', 'Hartford', 34, 110000],
53 | ['Danny Leon', 'Computer Scientist', 'Newark', 60, 220000],
54 | ['Lane Lee', 'Corporate Counselor', 'Cincinnati', 52, 180000],
55 | ['Jesse Hall', 'Business Analyst', 'Baltimore', 44, 99000],
56 | ['Danni Hudson', 'Agency Legal Counsel', 'Tampa', 37, 90000],
57 | ['Terry Macdonald', 'Commercial Specialist', 'Miami', 39, 140000],
58 | ['Justice Mccarthy', 'Attorney', 'Tucson', 26, 330000],
59 | ['Silver Carey', 'Computer Scientist', 'Memphis', 47, 250000],
60 | ['Franky Miles', 'Industrial Analyst', 'Buffalo', 49, 190000],
61 | ['Glen Nixon', 'Corporate Counselor', 'Arlington', 44, 80000],
62 | ['Gabby Strickland', 'Business Process Consultant', 'Scottsdale', 26, 45000],
63 | ['Mason Ray', 'Computer Scientist', 'San Francisco', 39, 142000],
64 | ];
65 |
66 | options = {
67 | filter: true,
68 | selectableRows: 'multiple',
69 | filterType: 'dropdown',
70 | responsive: 'standard',
71 | rowsPerPage: 10,
72 | download: false, // hide csv download option
73 | onTableInit: this.handleTableInit,
74 | onTableChange: this.handleTableChange,
75 | };
76 |
77 | render() {
78 | return ;
79 | }
80 | }
81 |
82 | export default Example;
83 |
--------------------------------------------------------------------------------
/examples/resizable-columns/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import ReactDOM from "react-dom";
3 | import MUIDataTable from "../../src/";
4 |
5 | import FormControl from '@mui/material/FormControl';
6 | import TextField from '@mui/material/TextField';
7 | import Switch from '@mui/material/Switch';
8 | import FormGroup from '@mui/material/FormGroup';
9 | import FormControlLabel from '@mui/material/FormControlLabel';
10 |
11 | function Example(props) {
12 |
13 | const [marginLeft, setMarginLeft] = useState(10);
14 | const [selectableRows, setSelectableRows] = useState("multiple");
15 |
16 | const [counter, setCounter] = useState(1);
17 | const incrCount = () => { // We update an arbitrary value here to test table resizing on state updates
18 | setCounter(counter + 1);
19 | };
20 |
21 | const columns = [
22 | {
23 | name: "Counter",
24 | options: {
25 | sort: false,
26 | empty: true,
27 | customBodyRender: value => +
28 | }
29 | },
30 | {
31 | name: "Name",
32 | options: {
33 | hint: "?",
34 | setCellProps: () => ({style: {whiteSpace:'nowrap'}})
35 | }
36 | },
37 | {
38 | name: "Business Title",
39 | options: {
40 | hint: "?",
41 | customBodyRender: (val) => {
42 | let parentStyle = {
43 | position: 'absolute',
44 | top: 0,
45 | right: 0,
46 | bottom: 0,
47 | left: 0,
48 | boxSizing: 'border-box',
49 | display: 'block',
50 | width: '100%',
51 | };
52 | let cellStyle = {
53 | boxSizing: 'border-box',
54 | overflow: 'hidden',
55 | textOverflow: 'ellipsis',
56 | whiteSpace: 'nowrap',
57 | };
58 | return (
59 |
60 |
61 |
62 | {val}
63 |
64 |
65 |
66 | );
67 | }
68 | }
69 | },
70 | "Location"
71 | ];
72 |
73 | const data = [
74 | ["Gabby George ", "Business Analyst", "Minneapolis"],
75 | ["Aiden Lloyd", "Business Consultant at Tony's Burger Palace and CEO of Johnny's Blueberry Sundaes", "Dallas"],
76 | ["Jaden Collins", "Attorney", "Santa Ana"],
77 | ["Franky Rees", "Business Analyst", "St. Petersburg"],
78 | ["Aaren Rose", null, "Toledo"]
79 | ];
80 |
81 | const options = {
82 | filter: true,
83 | filterType: 'dropdown',
84 | resizableColumns: true,
85 | selectableRows: selectableRows,
86 | draggableColumns: {
87 | enabled: true,
88 | }
89 | };
90 |
91 | return (
92 | <>
93 |
94 |
95 | setMarginLeft(e.target.value)} />
96 |
97 | setSelectableRows(event.target.checked ? "multiple" : "none")}
102 | value="true"
103 | color="primary"
104 | />
105 | }
106 | label="Selectable Rows"
107 | />
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | >
117 | );
118 | }
119 |
120 | export default Example;
121 |
--------------------------------------------------------------------------------
/examples/simple-no-toolbar/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import MUIDataTable from "../../src/";
4 |
5 | class Example extends React.Component {
6 | render() {
7 | const columns = ["Name", "Title", "Location"];
8 |
9 | const data = [
10 | ["Gabby George", "Business Analyst", "Minneapolis"],
11 | ["Aiden Lloyd", "Business Consultant", "Dallas"],
12 | ["Jaden Collins", "Attorney", "Santa Ana"],
13 | ["Franky Rees", "Business Analyst", "St. Petersburg"],
14 | ["Aaren Rose", null, "Toledo"]
15 | ];
16 |
17 | const options = {
18 | filter: false,
19 | search: false,
20 | print: false,
21 | download: false,
22 | viewColumns: false,
23 | customToolbar: null,
24 | responsive: 'vertical'
25 | };
26 |
27 | return (
28 |
29 | );
30 | }
31 | }
32 |
33 | export default Example;
34 |
--------------------------------------------------------------------------------
/examples/text-localization/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import MUIDataTable from "../../src/";
4 |
5 | class Example extends React.Component {
6 |
7 | render() {
8 |
9 | const columns = ["Name", "Title", "Location", "Age", "Salary"];
10 |
11 | const data = [
12 | ["Gabby George", "Business Analyst", "Minneapolis", 30, "$100,000"],
13 | ["Aiden Lloyd", "Business Consultant", "Dallas", 55, "$200,000"],
14 | ["Jaden Collins", "Attorney", "Santa Ana", 27, "$500,000"],
15 | ["Franky Rees", "Business Analyst", "St. Petersburg", 22, "$50,000"],
16 | ["Aaren Rose", "Business Consultant", "Toledo", 28, "$75,000"],
17 | ["Blake Duncan", "Business Management Analyst", "San Diego", 65, "$94,000"],
18 | ["Frankie Parry", "Agency Legal Counsel", "Jacksonville", 71, "$210,000"],
19 | ["Lane Wilson", "Commercial Specialist", "Omaha", 19, "$65,000"],
20 | ["Robin Duncan", "Business Analyst", "Los Angeles", 20, "$77,000"],
21 | ["Mel Brooks", "Business Consultant", "Oklahoma City", 37, "$135,000"],
22 | ["Harper White", "Attorney", "Pittsburgh", 52, "$420,000"],
23 | ["Kris Humphrey", "Agency Legal Counsel", "Laredo", 30, "$150,000"],
24 | ["Frankie Long", "Industrial Analyst", "Austin", 31, "$170,000"],
25 | ["Brynn Robbins", "Business Analyst", "Norfolk", 22, "$90,000"],
26 | ["Justice Mann", "Business Consultant", "Chicago", 24, "$133,000"],
27 | ["Addison Navarro", "Business Management Analyst", "New York", 50, "$295,000"],
28 | ["Jesse Welch", "Agency Legal Counsel", "Seattle", 28, "$200,000"],
29 | ["Eli Mejia", "Commercial Specialist", "Long Beach", 65, "$400,000"],
30 | ["Gene Leblanc", "Industrial Analyst", "Hartford", 34, "$110,000"],
31 | ["Danny Leon", "Computer Scientist", "Newark", 60, "$220,000"],
32 | ["Lane Lee", "Corporate Counselor", "Cincinnati", 52, "$180,000"],
33 | ["Jesse Hall", "Business Analyst", "Baltimore", 44, "$99,000"],
34 | ["Danni Hudson", "Agency Legal Counsel", "Tampa", 37, "$90,000"],
35 | ["Terry Macdonald", "Commercial Specialist", "Miami", 39, "$140,000"],
36 | ["Justice Mccarthy", "Attorney", "Tucson", 26, "$330,000"],
37 | ["Silver Carey", "Computer Scientist", "Memphis", 47, "$250,000" ],
38 | ["Franky Miles", "Industrial Analyst", "Buffalo", 49, "$190,000"],
39 | ["Glen Nixon", "Corporate Counselor", "Arlington", 44, "$80,000"],
40 | ["Gabby Strickland", "Business Process Consultant", "Scottsdale", 26, "$45,000"],
41 | ["Mason Ray", "Computer Scientist", "San Francisco", 39, "$142,000"]
42 | ];
43 |
44 | const options = {
45 | filter: true,
46 | filterType: 'dropdown',
47 | responsive: 'vertical',
48 | textLabels: {
49 | body: {
50 | noMatch: "Sorry we could not find any records!",
51 | },
52 | pagination:{
53 | next: "Following page",
54 | previous: "Preceding page"
55 | },
56 | filter: {
57 | all: "All Records",
58 | title: "OUR FILTERS",
59 | reset: "PERFORM RESET",
60 | },
61 | selectedRows: {
62 | text: "rows has been deleted",
63 | delete: "Delete Row",
64 | deleteAria: "Deleted Selected Rows"
65 | },
66 | }
67 | };
68 |
69 | return (
70 |
71 | );
72 |
73 | }
74 | }
75 |
76 | export default Example;
77 |
--------------------------------------------------------------------------------
/examples/themes/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import MUIDataTable from '../../src/';
3 | import { createTheme, ThemeProvider } from '@mui/material/styles';
4 |
5 | class Example extends React.Component {
6 | render() {
7 | const columns = [
8 | {
9 | name: 'Name',
10 | options: {
11 | filter: true,
12 | filterList: ['Franky Miles'],
13 | },
14 | },
15 | {
16 | name: 'Title',
17 | options: {
18 | filter: true,
19 | },
20 | },
21 | {
22 | name: 'Location',
23 | options: {
24 | filter: false,
25 | },
26 | },
27 | {
28 | name: 'Age',
29 | options: {
30 | filter: true,
31 | },
32 | },
33 | {
34 | name: 'Salary',
35 | options: {
36 | filter: true,
37 | sort: false,
38 | },
39 | },
40 | ];
41 | const data = [
42 | ['Gabby George', 'Business Analyst', 'Minneapolis', 30, 100000],
43 | ['Business Analyst', 'Business Consultant', 'Dallas', 55, 200000],
44 | ['Jaden Collins', 'Attorney', 'Santa Ana', 27, 500000],
45 | ['Franky Rees', 'Business Analyst', 'St. Petersburg', 22, 50000],
46 | ['Aaren Rose', 'Business Consultant', 'Toledo', 28, 75000],
47 | ['Blake Duncan', 'Business Management Analyst', 'San Diego', 65, 94000],
48 | ['Frankie Parry', 'Agency Legal Counsel', 'Jacksonville', 71, 210000],
49 | ['Lane Wilson', 'Commercial Specialist', 'Omaha', 19, 65000],
50 | ['Robin Duncan', 'Business Analyst', 'Los Angeles', 20, 77000],
51 | ['Mel Brooks', 'Business Consultant', 'Oklahoma City', 37, 135000],
52 | ['Harper White', 'Attorney', 'Pittsburgh', 52, 420000],
53 | ['Kris Humphrey', 'Agency Legal Counsel', 'Laredo', 30, 150000],
54 | ['Frankie Long', 'Industrial Analyst', 'Austin', 31, 170000],
55 | ['Brynn Robbins', 'Business Analyst', 'Norfolk', 22, 90000],
56 | ['Justice Mann', 'Business Consultant', 'Chicago', 24, 133000],
57 | ['Addison Navarro', 'Business Management Analyst', 'New York', 50, 295000],
58 | ['Jesse Welch', 'Agency Legal Counsel', 'Seattle', 28, 200000],
59 | ['Eli Mejia', 'Commercial Specialist', 'Long Beach', 65, 400000],
60 | ['Gene Leblanc', 'Industrial Analyst', 'Hartford', 34, 110000],
61 | ['Danny Leon', 'Computer Scientist', 'Newark', 60, 220000],
62 | ['Lane Lee', 'Corporate Counselor', 'Cincinnati', 52, 180000],
63 | ['Jesse Hall', 'Business Analyst', 'Baltimore', 44, 99000],
64 | ['Danni Hudson', 'Agency Legal Counsel', 'Tampa', 37, 90000],
65 | ['Terry Macdonald', 'Commercial Specialist', 'Miami', 39, 140000],
66 | ['Justice Mccarthy', 'Attorney', 'Tucson', 26, 330000],
67 | ['Silver Carey', 'Computer Scientist', 'Memphis', 47, 250000],
68 | ['Franky Miles', 'Industrial Analyst', 'Buffalo', 49, 190000],
69 | ['Glen Nixon', 'Corporate Counselor', 'Arlington', 44, 80000],
70 | ['Gabby Strickland', 'Business Process Consultant', 'Scottsdale', 26, 45000],
71 | ['Mason Ray', 'Computer Scientist', 'San Francisco', 39, 142000],
72 | ];
73 |
74 | const options = {
75 | filter: true,
76 | selectableRows: 'multiple',
77 | filterType: 'dropdown',
78 | responsive: 'vertical',
79 | rowsPerPage: 10,
80 | };
81 |
82 | const theme = createTheme({
83 | palette: { type: 'dark' },
84 | typography: { useNextVariants: true },
85 | });
86 |
87 | return (
88 |
89 |
90 |
91 | );
92 | }
93 | }
94 |
95 | export default Example;
96 |
--------------------------------------------------------------------------------
/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "hosting": {
3 | "public": "docs/export",
4 | "ignore": [
5 | "firebase.json",
6 | "**/.*",
7 | "**/node_modules/**"
8 | ]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MUIDatables Example
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | // next.config.js
2 | module.exports = {
3 | exportPathMap: function() {
4 | return {
5 | '/': { page: '/' },
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | printWidth: 120,
3 | singleQuote: true,
4 | trailingComma: 'all',
5 | bracketSpacing: true,
6 | jsxBracketSameLine: true,
7 | parser: 'babel',
8 | semi: true,
9 | };
10 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from '@rollup/plugin-babel';
2 | import commonjs from '@rollup/plugin-commonjs';
3 | import replace from '@rollup/plugin-replace';
4 | import uglify from '@lopatnov/rollup-plugin-uglify';
5 |
6 | export default {
7 | input: 'src/index.js',
8 | plugins: [
9 | replace({
10 | 'process.env.NODE_ENV': JSON.stringify('production'),
11 | }),
12 | commonjs({
13 | include: ['node_modules/**'],
14 | }),
15 | babel({
16 | babelHelpers: 'runtime',
17 | babelrc: true,
18 | }),
19 | uglify({
20 | compress: {
21 | conditionals: true,
22 | unused: true,
23 | comparisons: true,
24 | sequences: true,
25 | dead_code: true,
26 | evaluate: true,
27 | if_return: true,
28 | join_vars: true,
29 | },
30 | output: {
31 | comments: false,
32 | },
33 | }),
34 | ],
35 | output: {
36 | file: 'dist/index.js',
37 | format: 'cjs',
38 | exports: 'named',
39 | sourcemap: true,
40 | },
41 | };
42 |
--------------------------------------------------------------------------------
/src/components/ExpandButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import IconButton from '@mui/material/IconButton';
3 | import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
4 | import RemoveIcon from '@mui/icons-material/Remove';
5 |
6 | const ExpandButton = ({
7 | areAllRowsExpanded,
8 | buttonClass,
9 | expandableRowsHeader,
10 | expandedRows,
11 | iconClass,
12 | iconIndeterminateClass,
13 | isHeaderCell,
14 | onExpand,
15 | }) => {
16 | return (
17 | <>
18 | {isHeaderCell && !areAllRowsExpanded() && areAllRowsExpanded && expandedRows.data.length > 0 ? (
19 |
24 |
25 |
26 | ) : (
27 |
32 |
33 |
34 | )}
35 | >
36 | );
37 | };
38 |
39 | export default ExpandButton;
40 |
--------------------------------------------------------------------------------
/src/components/JumpToPage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import InputBase from '@mui/material/InputBase';
4 | import MenuItem from '@mui/material/MenuItem';
5 | import Select from '@mui/material/Select';
6 | import Toolbar from '@mui/material/Toolbar';
7 | import Typography from '@mui/material/Typography';
8 | import { makeStyles } from 'tss-react/mui';
9 | import { getPageValue } from '../utils.js';
10 | import clsx from 'clsx';
11 |
12 | const useStyles = makeStyles({ name: 'MUIDataTableJumpToPage' })(theme => ({
13 | root: {
14 | color: theme.palette.text.primary,
15 | },
16 | caption: {
17 | flexShrink: 0,
18 | },
19 | /* Styles applied to the Select component root element */
20 | selectRoot: {
21 | marginRight: 32,
22 | marginLeft: 8,
23 | },
24 | select: {
25 | paddingTop: 6,
26 | paddingBottom: 7,
27 | paddingLeft: 8,
28 | paddingRight: 24,
29 | textAlign: 'right',
30 | textAlignLast: 'right',
31 | fontSize: theme.typography.pxToRem(14),
32 | },
33 | /* Styles applied to Select component icon class */
34 | selectIcon: {},
35 | /* Styles applied to InputBase component */
36 | input: {
37 | color: 'inhert',
38 | fontSize: 'inhert',
39 | flexShrink: 0,
40 | },
41 | }));
42 |
43 | function JumpToPage(props) {
44 | const { classes } = useStyles();
45 |
46 | const handlePageChange = event => {
47 | props.changePage(parseInt(event.target.value, 10));
48 | };
49 |
50 | const { count, textLabels, rowsPerPage, page, changePage } = props;
51 |
52 | const textLabel = textLabels.pagination.jumpToPage;
53 |
54 | let pages = [];
55 | let lastPage = Math.min(1000, getPageValue(count, rowsPerPage, 1000000));
56 |
57 | for (let ii = 0; ii <= lastPage; ii++) {
58 | pages.push(ii);
59 | }
60 | const MenuItemComponent = MenuItem;
61 |
62 | let myStyle = {
63 | display: 'flex',
64 | minHeight: '52px',
65 | alignItems: 'center',
66 | };
67 |
68 | return (
69 |
70 |
71 | {textLabel}
72 |
73 | }
76 | value={getPageValue(count, rowsPerPage, page)}
77 | onChange={handlePageChange}
78 | style={{ marginRight: 0 }}>
79 | {pages.map(pageVal => (
80 |
81 | {pageVal + 1}
82 |
83 | ))}
84 |
85 |
86 | );
87 | }
88 |
89 | JumpToPage.propTypes = {
90 | count: PropTypes.number.isRequired,
91 | page: PropTypes.number.isRequired,
92 | rowsPerPage: PropTypes.number.isRequired,
93 | textLabels: PropTypes.object.isRequired,
94 | };
95 |
96 | export default JumpToPage;
97 |
--------------------------------------------------------------------------------
/src/components/Popover.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef, useState } from 'react';
2 | import PropTypes from 'prop-types';
3 | import MuiPopover from '@mui/material/Popover';
4 | import IconButton from '@mui/material/IconButton';
5 | import CloseIcon from '@mui/icons-material/Close';
6 |
7 | const Popover = ({ className, trigger, refExit, hide, content, ...providedProps }) => {
8 | const [isOpen, open] = useState(false);
9 | const anchorEl = useRef(null);
10 |
11 | useEffect(() => {
12 | if (isOpen) {
13 | const shouldHide = typeof hide === 'boolean' ? hide : false;
14 | if (shouldHide) {
15 | open(false);
16 | }
17 | }
18 | }, [hide, isOpen, open]);
19 |
20 | const handleClick = event => {
21 | anchorEl.current = event.currentTarget;
22 | open(true);
23 | };
24 |
25 | const handleRequestClose = () => {
26 | open(false);
27 | };
28 |
29 | const closeIconClass = providedProps.classes.closeIcon;
30 | delete providedProps.classes.closeIcon; // remove non-standard class from being passed to the popover component
31 |
32 | const transformOriginSpecs = {
33 | vertical: 'top',
34 | horizontal: 'center',
35 | };
36 |
37 | const anchorOriginSpecs = {
38 | vertical: 'bottom',
39 | horizontal: 'center',
40 | };
41 |
42 | const handleOnExit = () => {
43 | if (refExit) {
44 | refExit();
45 | }
46 | };
47 |
48 | const triggerProps = {
49 | key: 'content',
50 | onClick: event => {
51 | if (trigger.props.onClick) trigger.props.onClick();
52 | handleClick(event);
53 | },
54 | };
55 |
56 | return (
57 | <>
58 | {trigger}
59 |
68 |
73 |
74 |
75 | {content}
76 |
77 | >
78 | );
79 | };
80 |
81 | Popover.propTypes = {
82 | refExit: PropTypes.func,
83 | trigger: PropTypes.node.isRequired,
84 | content: PropTypes.node.isRequired,
85 | hide: PropTypes.bool,
86 | };
87 |
88 | export default Popover;
89 |
--------------------------------------------------------------------------------
/src/components/TableBodyRow.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import clsx from 'clsx';
4 | import TableRow from '@mui/material/TableRow';
5 | import { withStyles } from 'tss-react/mui';
6 |
7 | const defaultBodyRowStyles = theme => ({
8 | root: {
9 | // material v4
10 | '&.Mui-selected': {
11 | backgroundColor: theme.palette.action.selected,
12 | },
13 |
14 | // material v3 workaround
15 | '&.mui-row-selected': {
16 | backgroundColor: theme.palette.action.selected,
17 | },
18 | },
19 | hoverCursor: { cursor: 'pointer' },
20 | responsiveStacked: {
21 | [theme.breakpoints.down('md')]: {
22 | borderTop: 'solid 2px rgba(0, 0, 0, 0.15)',
23 | borderBottom: 'solid 2px rgba(0, 0, 0, 0.15)',
24 | padding: 0,
25 | margin: 0,
26 | },
27 | },
28 | responsiveSimple: {
29 | [theme.breakpoints.down('sm')]: {
30 | borderTop: 'solid 2px rgba(0, 0, 0, 0.15)',
31 | borderBottom: 'solid 2px rgba(0, 0, 0, 0.15)',
32 | padding: 0,
33 | margin: 0,
34 | },
35 | },
36 | });
37 |
38 | class TableBodyRow extends React.Component {
39 | static propTypes = {
40 | /** Options used to describe table */
41 | options: PropTypes.object.isRequired,
42 | /** Callback to execute when row is clicked */
43 | onClick: PropTypes.func,
44 | /** Current row selected or not */
45 | rowSelected: PropTypes.bool,
46 | /** Extend the style applied to components */
47 | classes: PropTypes.object,
48 | };
49 |
50 | render() {
51 | const { classes, options, rowSelected, onClick, className, isRowSelectable, ...rest } = this.props;
52 |
53 | var methods = {};
54 | if (onClick) {
55 | methods.onClick = onClick;
56 | }
57 |
58 | return (
59 |
78 | {this.props.children}
79 |
80 | );
81 | }
82 | }
83 |
84 | export default withStyles(TableBodyRow, defaultBodyRowStyles, { name: 'MUIDataTableBodyRow' });
85 |
--------------------------------------------------------------------------------
/src/components/TableFilterList.js:
--------------------------------------------------------------------------------
1 | import { makeStyles } from 'tss-react/mui';
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 | import TableFilterListItem from './TableFilterListItem';
5 |
6 | const useStyles = makeStyles({ name: 'MUIDataTableFilterList' })(() => ({
7 | root: {
8 | display: 'flex',
9 | justifyContent: 'left',
10 | flexWrap: 'wrap',
11 | margin: '0px 16px 0px 16px',
12 | },
13 | chip: {
14 | margin: '8px 8px 0px 0px',
15 | },
16 | }));
17 |
18 | const TableFilterList = ({
19 | options,
20 | filterList,
21 | filterUpdate,
22 | filterListRenderers,
23 | columnNames,
24 | serverSideFilterList,
25 | customFilterListUpdate,
26 | ItemComponent = TableFilterListItem,
27 | }) => {
28 | const { classes } = useStyles();
29 | const { serverSide } = options;
30 |
31 | const removeFilter = (index, filterValue, columnName, filterType, customFilterListUpdate = null) => {
32 | let removedFilter = filterValue;
33 | if (Array.isArray(removedFilter) && removedFilter.length === 0) {
34 | removedFilter = filterList[index];
35 | }
36 |
37 | filterUpdate(index, filterValue, columnName, filterType, customFilterListUpdate, filterList => {
38 | if (options.onFilterChipClose) {
39 | options.onFilterChipClose(index, removedFilter, filterList);
40 | }
41 | });
42 | };
43 | const customFilterChip = (customFilterItem, index, customFilterItemIndex, item, isArray) => {
44 | let type;
45 | // If our custom filter list is an array, we need to check for custom update functions to determine
46 | // default type. Otherwise we use the supplied type in options.
47 | if (isArray) {
48 | type = customFilterListUpdate[index] ? 'custom' : 'chip';
49 | } else {
50 | type = columnNames[index].filterType;
51 | }
52 |
53 | return (
54 |
58 | removeFilter(
59 | index,
60 | item[customFilterItemIndex] || [],
61 | columnNames[index].name,
62 | type,
63 | customFilterListUpdate[index],
64 | )
65 | }
66 | className={classes.chip}
67 | itemKey={customFilterItemIndex}
68 | index={index}
69 | data={item}
70 | columnNames={columnNames}
71 | filterProps={
72 | options.setFilterChipProps
73 | ? options.setFilterChipProps(index, columnNames[index].name, item[customFilterItemIndex] || [])
74 | : {}
75 | }
76 | />
77 | );
78 | };
79 |
80 | const filterChip = (index, data, colIndex) => (
81 | removeFilter(index, data, columnNames[index].name, 'chip')}
85 | className={classes.chip}
86 | itemKey={colIndex}
87 | index={index}
88 | data={data}
89 | columnNames={columnNames}
90 | filterProps={options.setFilterChipProps ? options.setFilterChipProps(index, columnNames[index].name, data) : {}}
91 | />
92 | );
93 |
94 | const getFilterList = filterList => {
95 | return filterList.map((item, index) => {
96 | if (columnNames[index].filterType === 'custom' && filterList[index].length) {
97 | const filterListRenderersValue = filterListRenderers[index](item);
98 |
99 | if (Array.isArray(filterListRenderersValue)) {
100 | return filterListRenderersValue.map((customFilterItem, customFilterItemIndex) =>
101 | customFilterChip(customFilterItem, index, customFilterItemIndex, item, true),
102 | );
103 | } else {
104 | return customFilterChip(filterListRenderersValue, index, index, item, false);
105 | }
106 | }
107 |
108 | return item.map((data, colIndex) => filterChip(index, data, colIndex));
109 | });
110 | };
111 |
112 | return (
113 |
114 | {serverSide && serverSideFilterList ? getFilterList(serverSideFilterList) : getFilterList(filterList)}
115 |
116 | );
117 | };
118 |
119 | TableFilterList.propTypes = {
120 | /** Data used to filter table against */
121 | filterList: PropTypes.array.isRequired,
122 | /** Filter List value renderers */
123 | filterListRenderers: PropTypes.array.isRequired,
124 | /** Columns used to describe table */
125 | columnNames: PropTypes.arrayOf(
126 | PropTypes.oneOfType([
127 | PropTypes.string,
128 | PropTypes.shape({ name: PropTypes.string.isRequired, filterType: PropTypes.string }),
129 | ]),
130 | ).isRequired,
131 | /** Callback to trigger filter update */
132 | onFilterUpdate: PropTypes.func,
133 | ItemComponent: PropTypes.any,
134 | };
135 |
136 | export default TableFilterList;
137 |
--------------------------------------------------------------------------------
/src/components/TableFilterListItem.js:
--------------------------------------------------------------------------------
1 | import Chip from '@mui/material/Chip';
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 | import clsx from 'clsx';
5 |
6 | const TableFilterListItem = ({ label, onDelete, className, filterProps }) => {
7 | filterProps = filterProps || {};
8 | if (filterProps.className) {
9 | className = clsx(className, filterProps.className);
10 | }
11 | return ;
12 | };
13 |
14 | TableFilterListItem.propTypes = {
15 | label: PropTypes.node,
16 | onDelete: PropTypes.func.isRequired,
17 | className: PropTypes.string.isRequired,
18 | };
19 |
20 | export default TableFilterListItem;
21 |
--------------------------------------------------------------------------------
/src/components/TableFooter.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import MuiTable from '@mui/material/Table';
3 | import TablePagination from './TablePagination';
4 | import { makeStyles } from 'tss-react/mui';
5 | import PropTypes from 'prop-types';
6 |
7 | const useStyles = makeStyles({ name: 'MUIDataTableFooter' })(() => ({
8 | root: {
9 | '@media print': {
10 | display: 'none',
11 | },
12 | },
13 | }));
14 |
15 | const TableFooter = ({ options, rowCount, page, rowsPerPage, changeRowsPerPage, changePage }) => {
16 | const { classes } = useStyles();
17 | const { customFooter, pagination = true } = options;
18 |
19 | if (customFooter) {
20 | return (
21 |
22 | {options.customFooter(
23 | rowCount,
24 | page,
25 | rowsPerPage,
26 | changeRowsPerPage,
27 | changePage,
28 | options.textLabels.pagination,
29 | )}
30 |
31 | );
32 | }
33 |
34 | if (pagination) {
35 | return (
36 |
37 |
46 |
47 | );
48 | }
49 |
50 | return null;
51 | };
52 |
53 | TableFooter.propTypes = {
54 | /** Total number of table rows */
55 | rowCount: PropTypes.number.isRequired,
56 | /** Options used to describe table */
57 | options: PropTypes.shape({
58 | customFooter: PropTypes.func,
59 | pagination: PropTypes.bool,
60 | textLabels: PropTypes.shape({
61 | pagination: PropTypes.object,
62 | }),
63 | }),
64 | /** Current page index */
65 | page: PropTypes.number.isRequired,
66 | /** Total number allowed of rows per page */
67 | rowsPerPage: PropTypes.number.isRequired,
68 | /** Callback to trigger rows per page change */
69 | changeRowsPerPage: PropTypes.func.isRequired,
70 | /** Callback to trigger page change */
71 | changePage: PropTypes.func.isRequired,
72 | };
73 |
74 | export default TableFooter;
75 |
--------------------------------------------------------------------------------
/src/components/TableHeadRow.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import clsx from 'clsx';
4 | import TableRow from '@mui/material/TableRow';
5 | import { makeStyles } from 'tss-react/mui';
6 |
7 | const useStyles = makeStyles({ name: 'MUIDataTableHeadRow' })(() => ({
8 | root: {},
9 | }));
10 |
11 | const TableHeadRow = ({ children }) => {
12 | const { classes } = useStyles();
13 |
14 | return (
15 |
19 | {children}
20 |
21 | );
22 | };
23 |
24 | TableHeadRow.propTypes = {
25 | children: PropTypes.node,
26 | };
27 |
28 | export default TableHeadRow;
29 |
--------------------------------------------------------------------------------
/src/components/TablePagination.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import MuiTableCell from '@mui/material/TableCell';
4 | import MuiTableRow from '@mui/material/TableRow';
5 | import MuiTableFooter from '@mui/material/TableFooter';
6 | import MuiTablePagination from '@mui/material/TablePagination';
7 | import JumpToPage from './JumpToPage';
8 | import { makeStyles } from 'tss-react/mui';
9 | import { getPageValue } from '../utils';
10 |
11 | const useStyles = makeStyles({ name: 'MUIDataTablePagination' })(theme => ({
12 | root: {},
13 | tableCellContainer: {
14 | padding: '0px 24px 0px 24px',
15 | },
16 | navContainer: {
17 | display: 'flex',
18 | justifyContent: 'flex-end',
19 | },
20 | toolbar: {},
21 | selectRoot: {},
22 | '@media screen and (max-width: 400px)': {
23 | toolbar: {
24 | '& span:nth-of-type(2)': {
25 | display: 'none',
26 | },
27 | },
28 | selectRoot: {
29 | marginRight: '8px',
30 | },
31 | },
32 | }));
33 |
34 | function TablePagination(props) {
35 | const { classes } = useStyles();
36 |
37 | const handleRowChange = event => {
38 | props.changeRowsPerPage(event.target.value);
39 | };
40 |
41 | const handlePageChange = (_, page) => {
42 | props.changePage(page);
43 | };
44 |
45 | const { count, options, rowsPerPage, page } = props;
46 | const textLabels = options.textLabels.pagination;
47 |
48 | return (
49 |
50 |
51 |
52 |
53 | {options.jumpToPage ? (
54 |
62 | ) : null}
63 | `${from}-${to} ${textLabels.displayRows} ${count}`}
76 | backIconButtonProps={{
77 | id: 'pagination-back',
78 | 'data-testid': 'pagination-back',
79 | 'aria-label': textLabels.previous,
80 | title: textLabels.previous || '',
81 | }}
82 | nextIconButtonProps={{
83 | id: 'pagination-next',
84 | 'data-testid': 'pagination-next',
85 | 'aria-label': textLabels.next,
86 | title: textLabels.next || '',
87 | }}
88 | SelectProps={{
89 | id: 'pagination-input',
90 | SelectDisplayProps: { id: 'pagination-rows', 'data-testid': 'pagination-rows' },
91 | MenuProps: {
92 | id: 'pagination-menu',
93 | 'data-testid': 'pagination-menu',
94 | MenuListProps: { id: 'pagination-menu-list', 'data-testid': 'pagination-menu-list' },
95 | },
96 | }}
97 | rowsPerPageOptions={options.rowsPerPageOptions}
98 | onPageChange={handlePageChange}
99 | onRowsPerPageChange={handleRowChange}
100 | />
101 |
102 |
103 |
104 |
105 | );
106 | }
107 |
108 | TablePagination.propTypes = {
109 | /** Total number of table rows */
110 | count: PropTypes.number.isRequired,
111 | /** Options used to describe table */
112 | options: PropTypes.object.isRequired,
113 | /** Current page index */
114 | page: PropTypes.number.isRequired,
115 | /** Total number allowed of rows per page */
116 | rowsPerPage: PropTypes.number.isRequired,
117 | /** Callback to trigger rows per page change */
118 | changeRowsPerPage: PropTypes.func.isRequired,
119 | };
120 |
121 | export default TablePagination;
122 |
--------------------------------------------------------------------------------
/src/components/TableSearch.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Grow from '@mui/material/Grow';
3 | import TextField from '@mui/material/TextField';
4 | import SearchIcon from '@mui/icons-material/Search';
5 | import IconButton from '@mui/material/IconButton';
6 | import ClearIcon from '@mui/icons-material/Clear';
7 | import { makeStyles } from 'tss-react/mui';
8 |
9 | const useStyles = makeStyles({ name: 'MUIDataTableSearch' })(theme => ({
10 | main: {
11 | display: 'flex',
12 | flex: '1 0 auto',
13 | alignItems: 'center',
14 | },
15 | searchIcon: {
16 | color: theme.palette.text.secondary,
17 | marginRight: '8px',
18 | },
19 | searchText: {
20 | flex: '0.8 0',
21 | },
22 | clearIcon: {
23 | '&:hover': {
24 | color: theme.palette.error.main,
25 | },
26 | },
27 | }));
28 |
29 | const TableSearch = ({ options, searchText, onSearch, onHide }) => {
30 | const { classes } = useStyles();
31 |
32 | const handleTextChange = event => {
33 | onSearch(event.target.value);
34 | };
35 |
36 | const onKeyDown = event => {
37 | if (event.key === 'Escape') {
38 | onHide();
39 | }
40 | };
41 |
42 | const clearIconVisibility = options.searchAlwaysOpen ? 'hidden' : 'visible';
43 |
44 | return (
45 |
46 |
47 |
48 |
65 |
66 |
67 |
68 |
69 |
70 | );
71 | };
72 |
73 | export default TableSearch;
74 |
--------------------------------------------------------------------------------
/src/components/TableSelectCell.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import clsx from 'clsx';
4 | import Checkbox from '@mui/material/Checkbox';
5 | import TableCell from '@mui/material/TableCell';
6 | import { makeStyles } from 'tss-react/mui';
7 | import ExpandButton from './ExpandButton';
8 |
9 | const useStyles = makeStyles({ name: 'MUIDataTableSelectCell' })(theme => ({
10 | root: {
11 | '@media print': {
12 | display: 'none',
13 | },
14 | },
15 | fixedHeader: {
16 | position: 'sticky',
17 | top: '0px',
18 | zIndex: 100,
19 | },
20 | fixedLeft: {
21 | position: 'sticky',
22 | left: '0px',
23 | zIndex: 100,
24 | },
25 | icon: {
26 | cursor: 'pointer',
27 | transition: 'transform 0.25s',
28 | },
29 | expanded: {
30 | transform: 'rotate(90deg)',
31 | },
32 | hide: {
33 | visibility: 'hidden',
34 | },
35 | headerCell: {
36 | zIndex: 110,
37 | backgroundColor: theme.palette.background.paper,
38 | },
39 | expandDisabled: {},
40 | checkboxRoot: {},
41 | checked: {},
42 | disabled: {},
43 | }));
44 |
45 | const TableSelectCell = ({
46 | fixedHeader,
47 | fixedSelectColumn,
48 | isHeaderCell = false,
49 | expandableOn = false,
50 | selectableOn = 'none',
51 | isRowExpanded = false,
52 | onExpand,
53 | isRowSelectable,
54 | selectableRowsHeader,
55 | hideExpandButton,
56 | expandableRowsHeader,
57 | expandedRows,
58 | areAllRowsExpanded = () => false,
59 | selectableRowsHideCheckboxes,
60 | setHeadCellRef,
61 | dataIndex,
62 | components = {},
63 | ...otherProps
64 | }) => {
65 | const { classes } = useStyles();
66 | const CheckboxComponent = components.Checkbox || Checkbox;
67 | const ExpandButtonComponent = components.ExpandButton || ExpandButton;
68 |
69 | if (expandableOn === false && (selectableOn === 'none' || selectableRowsHideCheckboxes === true)) {
70 | return null;
71 | }
72 |
73 | const cellClass = clsx({
74 | [classes.root]: true,
75 | [classes.fixedHeader]: fixedHeader && isHeaderCell,
76 | [classes.fixedLeft]: fixedSelectColumn,
77 | [classes.headerCell]: isHeaderCell,
78 | });
79 |
80 | const buttonClass = clsx({
81 | [classes.expandDisabled]: hideExpandButton,
82 | });
83 |
84 | const iconClass = clsx({
85 | [classes.icon]: true,
86 | [classes.hide]: isHeaderCell && !expandableRowsHeader,
87 | [classes.expanded]: isRowExpanded || (isHeaderCell && areAllRowsExpanded()),
88 | });
89 | const iconIndeterminateClass = clsx({
90 | [classes.icon]: true,
91 | [classes.hide]: isHeaderCell && !expandableRowsHeader,
92 | });
93 |
94 | let refProp = {};
95 | if (setHeadCellRef) {
96 | refProp.ref = el => {
97 | setHeadCellRef(0, 0, el);
98 | };
99 | }
100 |
101 | const renderCheckBox = () => {
102 | if (isHeaderCell && (selectableOn !== 'multiple' || selectableRowsHeader === false)) {
103 | // only display the header checkbox for multiple selection.
104 | return null;
105 | }
106 | return (
107 |
119 | );
120 | };
121 |
122 | return (
123 |
124 |
125 | {expandableOn && (
126 |
137 | )}
138 | {selectableOn !== 'none' && selectableRowsHideCheckboxes !== true && renderCheckBox()}
139 |
140 |
141 | );
142 | };
143 |
144 | TableSelectCell.propTypes = {
145 | /** Select cell checked on/off */
146 | checked: PropTypes.bool.isRequired,
147 | /** Select cell part of fixed header */
148 | fixedHeader: PropTypes.bool,
149 | /** Callback to trigger cell update */
150 | onChange: PropTypes.func,
151 | /** Extend the style applied to components */
152 | classes: PropTypes.object,
153 | /** Is expandable option enabled */
154 | expandableOn: PropTypes.bool,
155 | /** Adds extra class, `expandDisabled` when the row is not expandable. */
156 | hideExpandButton: PropTypes.bool,
157 | /** Is selectable option enabled */
158 | selectableOn: PropTypes.string,
159 | /** Select cell disabled on/off */
160 | isRowSelectable: PropTypes.bool,
161 | };
162 |
163 | export default TableSelectCell;
164 |
--------------------------------------------------------------------------------
/src/components/TableToolbarSelect.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Paper from '@mui/material/Paper';
4 | import IconButton from '@mui/material/IconButton';
5 | import Typography from '@mui/material/Typography';
6 | import DeleteIcon from '@mui/icons-material/Delete';
7 | import { withStyles } from 'tss-react/mui';
8 | import MuiTooltip from '@mui/material/Tooltip';
9 |
10 | const defaultToolbarSelectStyles = theme => ({
11 | root: {
12 | backgroundColor: theme.palette.background.default,
13 | flex: '1 1 100%',
14 | display: 'flex',
15 | position: 'relative',
16 | zIndex: 120,
17 | justifyContent: 'space-between',
18 | alignItems: 'center',
19 | paddingTop: typeof theme.spacing === 'function' ? theme.spacing(1) : theme.spacing.unit,
20 | paddingBottom: typeof theme.spacing === 'function' ? theme.spacing(1) : theme.spacing.unit,
21 | '@media print': {
22 | display: 'none',
23 | },
24 | },
25 | title: {
26 | paddingLeft: '26px',
27 | },
28 | iconButton: {
29 | marginRight: '24px',
30 | },
31 | deleteIcon: {},
32 | });
33 |
34 | class TableToolbarSelect extends React.Component {
35 | static propTypes = {
36 | /** Options used to describe table */
37 | options: PropTypes.object.isRequired,
38 | /** Current row selected or not */
39 | rowSelected: PropTypes.bool,
40 | /** Callback to trigger selected rows delete */
41 | onRowsDelete: PropTypes.func,
42 | /** Extend the style applied to components */
43 | classes: PropTypes.object,
44 | };
45 |
46 | /**
47 | * @param {number[]} selectedRows Array of rows indexes that are selected, e.g. [0, 2] will select first and third rows in table
48 | */
49 | handleCustomSelectedRows = selectedRows => {
50 | if (!Array.isArray(selectedRows)) {
51 | throw new TypeError(`"selectedRows" must be an "array", but it's "${typeof selectedRows}"`);
52 | }
53 |
54 | if (selectedRows.some(row => typeof row !== 'number')) {
55 | throw new TypeError(`Array "selectedRows" must contain only numbers`);
56 | }
57 |
58 | const { options } = this.props;
59 | if (selectedRows.length > 1 && options.selectableRows === 'single') {
60 | throw new Error('Can not select more than one row when "selectableRows" is "single"');
61 | }
62 | this.props.selectRowUpdate('custom', selectedRows);
63 | };
64 |
65 | render() {
66 | const { classes, onRowsDelete, selectedRows, options, displayData, components = {} } = this.props;
67 | const textLabels = options.textLabels.selectedRows;
68 | const Tooltip = components.Tooltip || MuiTooltip;
69 |
70 | return (
71 |
72 |
73 |
74 | {selectedRows.data.length} {textLabels.text}
75 |
76 |
77 | {options.customToolbarSelect ? (
78 | options.customToolbarSelect(selectedRows, displayData, this.handleCustomSelectedRows)
79 | ) : (
80 |
81 |
82 |
83 |
84 |
85 | )}
86 |
87 | );
88 | }
89 | }
90 |
91 | export default withStyles(TableToolbarSelect, defaultToolbarSelectStyles, { name: 'MUIDataTableToolbarSelect' });
92 |
--------------------------------------------------------------------------------
/src/components/TableViewCol.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Checkbox from '@mui/material/Checkbox';
4 | import Typography from '@mui/material/Typography';
5 | import FormControl from '@mui/material/FormControl';
6 | import FormGroup from '@mui/material/FormGroup';
7 | import FormControlLabel from '@mui/material/FormControlLabel';
8 | import { makeStyles } from 'tss-react/mui';
9 |
10 | const useStyles = makeStyles({ name: 'MUIDataTableViewCol' })(theme => ({
11 | root: {
12 | padding: '16px 24px 16px 24px',
13 | fontFamily: 'Roboto',
14 | },
15 | title: {
16 | marginLeft: '-7px',
17 | marginRight: '24px',
18 | fontSize: '14px',
19 | color: theme.palette.text.secondary,
20 | textAlign: 'left',
21 | fontWeight: 500,
22 | },
23 | formGroup: {
24 | marginTop: '8px',
25 | },
26 | formControl: {},
27 | checkbox: {
28 | padding: '0px',
29 | width: '32px',
30 | height: '32px',
31 | },
32 | checkboxRoot: {},
33 | checked: {},
34 | label: {
35 | fontSize: '15px',
36 | marginLeft: '8px',
37 | color: theme.palette.text.primary,
38 | },
39 | }));
40 |
41 | const TableViewCol = ({ columns, options, components = {}, onColumnUpdate, updateColumns }) => {
42 | const { classes } = useStyles();
43 | const textLabels = options.textLabels.viewColumns;
44 | const CheckboxComponent = components.Checkbox || Checkbox;
45 |
46 | const handleColChange = index => {
47 | onColumnUpdate(index);
48 | };
49 |
50 | return (
51 |
52 |
53 | {textLabels.title}
54 |
55 |
56 | {columns.map((column, index) => {
57 | return (
58 | column.display !== 'excluded' &&
59 | column.viewColumns !== false && (
60 | handleColChange(index)}
76 | checked={column.display === 'true'}
77 | value={column.name}
78 | />
79 | }
80 | label={column.label}
81 | />
82 | )
83 | );
84 | })}
85 |
86 |
87 | );
88 | };
89 |
90 | TableViewCol.propTypes = {
91 | /** Columns used to describe table */
92 | columns: PropTypes.array.isRequired,
93 | /** Options used to describe table */
94 | options: PropTypes.object.isRequired,
95 | /** Callback to trigger View column update */
96 | onColumnUpdate: PropTypes.func,
97 | /** Extend the style applied to components */
98 | classes: PropTypes.object,
99 | };
100 |
101 | export default TableViewCol;
102 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './MUIDataTable';
2 | export { default as Popover } from './components/Popover';
3 | export { default as TableBodyCell } from './components/TableBodyCell';
4 | export { default as TableBody } from './components/TableBody';
5 | export { default as TableBodyRow } from './components/TableBodyRow';
6 | export { default as TableFilter } from './components/TableFilter';
7 | export { default as TableFilterList } from './components/TableFilterList';
8 | export { default as TableFooter } from './components/TableFooter';
9 | export { default as TableHeadCell } from './components/TableHeadCell';
10 | export { default as TableHead } from './components/TableHead';
11 | export { default as TableHeadRow } from './components/TableHeadRow';
12 | export { default as TablePagination } from './components/TablePagination';
13 | export { default as TableResize } from './components/TableResize';
14 | export { default as TableSearch } from './components/TableSearch';
15 | export { default as TableSelectCell } from './components/TableSelectCell';
16 | export { default as TableToolbar } from './components/TableToolbar';
17 | export { default as TableToolbarSelect } from './components/TableToolbarSelect';
18 | export { default as TableViewCol } from './components/TableViewCol';
19 | export { default as ExpandButton } from './components/ExpandButton';
20 | export { debounceSearchRender, DebounceTableSearch } from './plug-ins/DebounceSearchRender';
21 |
--------------------------------------------------------------------------------
/src/localStorage/index.js:
--------------------------------------------------------------------------------
1 | export { load } from './load';
2 | export { save } from './save';
3 |
--------------------------------------------------------------------------------
/src/localStorage/load.js:
--------------------------------------------------------------------------------
1 | const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
2 |
3 | export const load = storageKey => {
4 | if (isBrowser) {
5 | return JSON.parse(window.localStorage.getItem(storageKey));
6 | } else if (storageKey !== undefined) {
7 | console.warn('storageKey support only on browser');
8 | return undefined;
9 | }
10 | };
11 |
--------------------------------------------------------------------------------
/src/localStorage/save.js:
--------------------------------------------------------------------------------
1 | export const save = (storageKey, state) => {
2 | const { selectedRows, data, displayData, ...savedState } = state;
3 |
4 | window.localStorage.setItem(storageKey, JSON.stringify(savedState));
5 | };
6 |
--------------------------------------------------------------------------------
/src/plug-ins/DebounceSearchRender.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Grow from '@mui/material/Grow';
3 | import TextField from '@mui/material/TextField';
4 | import SearchIcon from '@mui/icons-material/Search';
5 | import IconButton from '@mui/material/IconButton';
6 | import ClearIcon from '@mui/icons-material/Clear';
7 | import { withStyles } from 'tss-react/mui';
8 |
9 | function debounce(func, wait, immediate) {
10 | var timeout;
11 | return function() {
12 | var context = this,
13 | args = arguments;
14 | var later = function() {
15 | timeout = null;
16 | if (!immediate) func.apply(context, args);
17 | };
18 | var callNow = immediate && !timeout;
19 | clearTimeout(timeout);
20 | timeout = setTimeout(later, wait);
21 | if (callNow) func.apply(context, args);
22 | };
23 | }
24 |
25 | const defaultStyles = theme => ({
26 | main: {
27 | display: 'flex',
28 | flex: '1 0 auto',
29 | alignItems: 'center',
30 | },
31 | searchIcon: {
32 | color: theme.palette.text.secondary,
33 | marginRight: '8px',
34 | },
35 | searchText: {
36 | flex: '0.8 0',
37 | },
38 | clearIcon: {
39 | '&:hover': {
40 | color: theme.palette.error.main,
41 | },
42 | },
43 | });
44 |
45 | class _DebounceTableSearch extends React.Component {
46 | handleTextChangeWrapper = debouncedSearch => {
47 | return function(event) {
48 | debouncedSearch(event.target.value);
49 | };
50 | };
51 |
52 | componentDidMount() {
53 | document.addEventListener('keydown', this.onKeyDown, false);
54 | }
55 |
56 | componentWillUnmount() {
57 | document.removeEventListener('keydown', this.onKeyDown, false);
58 | }
59 |
60 | onKeyDown = event => {
61 | if (event.keyCode === 27) {
62 | this.props.onHide();
63 | }
64 | };
65 |
66 | render() {
67 | const { classes, options, onHide, searchText, debounceWait } = this.props;
68 |
69 | const debouncedSearch = debounce(value => {
70 | this.props.onSearch(value);
71 | }, debounceWait);
72 |
73 | const clearIconVisibility = options.searchAlwaysOpen ? 'hidden' : 'visible';
74 |
75 | return (
76 |
77 |
78 |
79 | (this.searchField = el)}
91 | placeholder={options.searchPlaceholder}
92 | {...(options.searchProps ? options.searchProps : {})}
93 | />
94 |
95 |
96 |
97 |
98 |
99 | );
100 | }
101 | }
102 |
103 | var DebounceTableSearch = withStyles(_DebounceTableSearch, defaultStyles, { name: 'MUIDataTableSearch' });
104 | export { DebounceTableSearch };
105 |
106 | export function debounceSearchRender(debounceWait = 200) {
107 | return (searchText, handleSearch, hideSearch, options) => {
108 | return (
109 |
116 | );
117 | };
118 | }
119 |
--------------------------------------------------------------------------------
/src/textLabels.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Default text labels.
3 | */
4 | const getTextLabels = () => ({
5 | body: {
6 | noMatch: 'Sorry, no matching records found',
7 | toolTip: 'Sort',
8 | },
9 | pagination: {
10 | next: 'Next Page',
11 | previous: 'Previous Page',
12 | rowsPerPage: 'Rows per page:',
13 | displayRows: 'of',
14 | jumpToPage: 'Jump to Page:',
15 | },
16 | toolbar: {
17 | search: 'Search',
18 | downloadCsv: 'Download CSV',
19 | print: 'Print',
20 | viewColumns: 'View Columns',
21 | filterTable: 'Filter Table',
22 | },
23 | filter: {
24 | all: 'All',
25 | title: 'FILTERS',
26 | reset: 'RESET',
27 | },
28 | viewColumns: {
29 | title: 'Show Columns',
30 | titleAria: 'Show/Hide Table Columns',
31 | },
32 | selectedRows: {
33 | text: 'row(s) selected',
34 | delete: 'Delete',
35 | deleteAria: 'Delete Selected Rows',
36 | },
37 | });
38 |
39 | export default getTextLabels;
40 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | function buildMap(rows) {
2 | return rows.reduce((accum, { dataIndex }) => {
3 | accum[dataIndex] = true;
4 | return accum;
5 | }, {});
6 | }
7 |
8 | function escapeDangerousCSVCharacters(data) {
9 | if (typeof data === 'string') {
10 | // Places single quote before the appearance of dangerous characters if they
11 | // are the first in the data string.
12 | return data.replace(/^\+|^\-|^\=|^\@/g, "'$&");
13 | }
14 |
15 | return data;
16 | }
17 |
18 | function warnDeprecated(warning, consoleWarnings = true) {
19 | let consoleWarn = typeof consoleWarnings === 'function' ? consoleWarnings : console.warn;
20 | if (consoleWarnings) {
21 | consoleWarn(`Deprecation Notice: ${warning}`);
22 | }
23 | }
24 |
25 | function warnInfo(warning, consoleWarnings = true) {
26 | let consoleWarn = typeof consoleWarnings === 'function' ? consoleWarnings : console.warn;
27 | if (consoleWarnings) {
28 | consoleWarn(`${warning}`);
29 | }
30 | }
31 |
32 | function getPageValue(count, rowsPerPage, page) {
33 | const totalPages = count <= rowsPerPage ? 1 : Math.ceil(count / rowsPerPage);
34 |
35 | // `page` is 0-indexed
36 | return page >= totalPages ? totalPages - 1 : page;
37 | }
38 |
39 | function getCollatorComparator() {
40 | if (!!Intl) {
41 | const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
42 | return collator.compare;
43 | }
44 |
45 | const fallbackComparator = (a, b) => a.localeCompare(b);
46 | return fallbackComparator;
47 | }
48 |
49 | function sortCompare(order) {
50 | return (a, b) => {
51 | var aData = a.data === null || typeof a.data === 'undefined' ? '' : a.data;
52 | var bData = b.data === null || typeof b.data === 'undefined' ? '' : b.data;
53 | return (
54 | (typeof aData.localeCompare === 'function' ? aData.localeCompare(bData) : aData - bData) *
55 | (order === 'asc' ? 1 : -1)
56 | );
57 | };
58 | }
59 |
60 | function buildCSV(columns, data, options) {
61 | const replaceDoubleQuoteInString = columnData =>
62 | typeof columnData === 'string' ? columnData.replace(/\"/g, '""') : columnData;
63 |
64 | const buildHead = columns => {
65 | return (
66 | columns
67 | .reduce(
68 | (soFar, column) =>
69 | column.download
70 | ? soFar +
71 | '"' +
72 | escapeDangerousCSVCharacters(replaceDoubleQuoteInString(column.label || column.name)) +
73 | '"' +
74 | options.downloadOptions.separator
75 | : soFar,
76 | '',
77 | )
78 | .slice(0, -1) + '\r\n'
79 | );
80 | };
81 | const CSVHead = buildHead(columns);
82 |
83 | const buildBody = data => {
84 | if (!data.length) return '';
85 | return data
86 | .reduce(
87 | (soFar, row) =>
88 | soFar +
89 | '"' +
90 | row.data
91 | .filter((_, index) => columns[index].download)
92 | .map(columnData => escapeDangerousCSVCharacters(replaceDoubleQuoteInString(columnData)))
93 | .join('"' + options.downloadOptions.separator + '"') +
94 | '"\r\n',
95 | '',
96 | )
97 | .trim();
98 | };
99 | const CSVBody = buildBody(data);
100 |
101 | const csv = options.onDownload
102 | ? options.onDownload(buildHead, buildBody, columns, data)
103 | : `${CSVHead}${CSVBody}`.trim();
104 |
105 | return csv;
106 | }
107 |
108 | function downloadCSV(csv, filename) {
109 | const blob = new Blob([csv], { type: 'text/csv' });
110 |
111 | /* taken from react-csv */
112 | if (navigator && navigator.msSaveOrOpenBlob) {
113 | navigator.msSaveOrOpenBlob(blob, filename);
114 | } else {
115 | const dataURI = `data:text/csv;charset=utf-8,${csv}`;
116 |
117 | const URL = window.URL || window.webkitURL;
118 | const downloadURI = typeof URL.createObjectURL === 'undefined' ? dataURI : URL.createObjectURL(blob);
119 |
120 | let link = document.createElement('a');
121 | link.setAttribute('href', downloadURI);
122 | link.setAttribute('download', filename);
123 | document.body.appendChild(link);
124 | link.click();
125 | document.body.removeChild(link);
126 | }
127 | }
128 |
129 | function createCSVDownload(columns, data, options, downloadCSV) {
130 | const csv = buildCSV(columns, data, options);
131 |
132 | if (options.onDownload && csv === false) {
133 | return;
134 | }
135 |
136 | downloadCSV(csv, options.downloadOptions.filename);
137 | }
138 |
139 | export {
140 | buildMap,
141 | getPageValue,
142 | getCollatorComparator,
143 | sortCompare,
144 | createCSVDownload,
145 | buildCSV,
146 | downloadCSV,
147 | warnDeprecated,
148 | warnInfo,
149 | escapeDangerousCSVCharacters,
150 | };
151 |
--------------------------------------------------------------------------------
/test/MUIDataTableBodyCell.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { spy, stub } from 'sinon';
3 | import { mount, shallow } from 'enzyme';
4 | import { assert, expect, should } from 'chai';
5 | import MUIDataTable from '../src/MUIDataTable';
6 | import TableBodyCell from '../src/components/TableBodyCell';
7 |
8 | describe(' ', function() {
9 | let data;
10 | let columns;
11 |
12 | before(() => {
13 | columns = [
14 | {
15 | name: 'Name',
16 | },
17 | 'Company',
18 | { name: 'City', label: 'City Label', options: { filterType: 'textField' } },
19 | {
20 | name: 'State',
21 | options: { filterType: 'multiselect' },
22 | },
23 | { name: 'Empty', options: { empty: true, filterType: 'checkbox' } },
24 | ];
25 | data = [
26 | ['Joe James', 'Test Corp', 'Yonkers', 'NY'],
27 | ['John Walsh', 'Test Corp', 'Hartford', null],
28 | ['Bob Herm', 'Test Corp X', 'Tampa', 'FL'],
29 | ['James Houston', 'Test Corp', 'Dallas', 'TX'],
30 | ];
31 | });
32 |
33 | it('should execute "onCellClick" prop when clicked if provided', () => {
34 | var clickCount = 0;
35 | var colIndex, rowIndex, colData;
36 | const options = {
37 | onCellClick: (val, colMeta) => {
38 | clickCount++;
39 | colIndex = colMeta.colIndex;
40 | rowIndex = colMeta.rowIndex;
41 | colData = val;
42 | },
43 | };
44 |
45 | const fullWrapper = mount( );
46 |
47 | fullWrapper
48 | .find('[data-testid="MuiDataTableBodyCell-0-0"]')
49 | .at(0)
50 | .simulate('click');
51 | assert.strictEqual(clickCount, 1);
52 | assert.strictEqual(colIndex, 0);
53 | assert.strictEqual(rowIndex, 0);
54 | assert.strictEqual(colData, 'Joe James');
55 |
56 | fullWrapper
57 | .find('[data-testid="MuiDataTableBodyCell-2-3"]')
58 | .at(0)
59 | .simulate('click');
60 | assert.strictEqual(clickCount, 2);
61 | assert.strictEqual(colIndex, 2);
62 | assert.strictEqual(rowIndex, 3);
63 | assert.strictEqual(colData, 'Dallas');
64 |
65 | fullWrapper
66 | .find('[data-testid="MuiDataTableBodyCell-1-2"]')
67 | .at(0)
68 | .simulate('click');
69 | assert.strictEqual(clickCount, 3);
70 | assert.strictEqual(colIndex, 1);
71 | assert.strictEqual(rowIndex, 2);
72 | assert.strictEqual(colData, 'Test Corp X');
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/test/MUIDataTableCustomComponents.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { mount, shallow } from 'enzyme';
3 | import { assert } from 'chai';
4 | import MUIDataTable from '../src/MUIDataTable';
5 | import Chip from '@mui/material/Chip';
6 | import TableFilterList from '../src/components/TableFilterList';
7 |
8 | const CustomChip = props => {
9 | return ;
10 | };
11 |
12 | const CustomFilterList = props => {
13 | return ;
14 | };
15 |
16 | describe(' with custom components', function() {
17 | let data;
18 | let columns;
19 |
20 | before(() => {
21 | columns = [
22 | { name: 'Name' },
23 | {
24 | name: 'Company',
25 | options: {
26 | filter: true,
27 | filterType: 'custom',
28 | filterList: ['Test Corp'],
29 | },
30 | },
31 | { name: 'City', label: 'City Label' },
32 | { name: 'State' },
33 | { name: 'Empty', options: { empty: true, filterType: 'checkbox' } },
34 | ];
35 | data = [
36 | ['Joe James', 'Test Corp', 'Yonkers', 'NY'],
37 | ['John Walsh', 'Test Corp', 'Hartford', null],
38 | ['Bob Herm', 'Test Corp', 'Tampa', 'FL'],
39 | ['James Houston', 'Test Corp', 'Dallas', 'TX'],
40 | ];
41 | });
42 |
43 | it('should render a table with custom Chip in TableFilterList', () => {
44 | const wrapper = mount(
45 | ,
52 | );
53 | const customFilterList = wrapper.find(CustomFilterList);
54 | assert.lengthOf(customFilterList, 1);
55 | const customChip = customFilterList.find(CustomChip);
56 | assert.lengthOf(customChip, 1);
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/test/MUIDataTableFooter.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { spy } from 'sinon';
3 | import { mount } from 'enzyme';
4 | import { assert } from 'chai';
5 | import MuiTableFooter from '@mui/material/TableFooter';
6 | import getTextLabels from '../src/textLabels';
7 | import TableFooter from '../src/components/TableFooter';
8 | import JumpToPage from '../src/components/JumpToPage';
9 |
10 | describe(' ', function() {
11 | let options;
12 | const changeRowsPerPage = spy();
13 | const changePage = spy();
14 | before(() => {
15 | options = {
16 | rowsPerPageOptions: [5, 10, 15],
17 | textLabels: getTextLabels(),
18 | };
19 | });
20 |
21 | it('should render a table footer', () => {
22 | const mountWrapper = mount(
23 | ,
31 | );
32 |
33 | const actualResult = mountWrapper.find(MuiTableFooter);
34 | assert.strictEqual(actualResult.length, 1);
35 | });
36 |
37 | it('should render a table footer with customFooter', () => {
38 | const customOptions = {
39 | rowsPerPageOptions: [5, 10, 15],
40 | textLabels: getTextLabels(),
41 | customFooter: (rowCount, page, rowsPerPage, changeRowsPerPage, changePage, textLabels) => {
42 | return (
43 |
51 | );
52 | },
53 | };
54 |
55 | const mountWrapper = mount(
56 | ,
64 | );
65 |
66 | const actualResult = mountWrapper.find(MuiTableFooter);
67 | assert.strictEqual(actualResult.length, 1);
68 | });
69 |
70 | it('should not render a table footer', () => {
71 | const nonPageOption = {
72 | rowsPerPageOptions: [5, 10, 15],
73 | textLabels: getTextLabels(),
74 | pagination: false,
75 | };
76 |
77 | const mountWrapper = mount(
78 | ,
86 | );
87 |
88 | const actualResult = mountWrapper.find(MuiTableFooter);
89 | assert.strictEqual(actualResult.length, 0);
90 | });
91 |
92 | it('should render a JumpToPage component', () => {
93 | const options = {
94 | rowsPerPageOptions: [5, 10, 15],
95 | textLabels: getTextLabels(),
96 | jumpToPage: true,
97 | };
98 |
99 | const mountWrapper = mount(
100 | ,
108 | );
109 |
110 | const actualResult = mountWrapper.find(JumpToPage);
111 | assert.strictEqual(actualResult.length, 1);
112 | });
113 | });
114 |
--------------------------------------------------------------------------------
/test/MUIDataTableHeadCell.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { spy, stub } from 'sinon';
3 | import { mount, shallow } from 'enzyme';
4 | import { assert } from 'chai';
5 | import getTextLabels from '../src/textLabels';
6 | import TableHeadCell from '../src/components/TableHeadCell';
7 | import TableCell from '@mui/material/TableCell';
8 | import TableSortLabel from '@mui/material/TableSortLabel';
9 | import HelpIcon from '@mui/icons-material/Help';
10 | import { DndProvider } from 'react-dnd';
11 | import { HTML5Backend } from 'react-dnd-html5-backend';
12 |
13 | describe(' ', function() {
14 | let classes;
15 |
16 | before(() => {
17 | classes = {
18 | root: {},
19 | };
20 | });
21 |
22 | it('should add custom props to header cell if "setCellHeaderProps" provided', () => {
23 | const options = { sort: true, textLabels: getTextLabels() };
24 | const toggleSort = () => {};
25 | const setCellHeaderProps = { myProp: 'test', className: 'testClass' };
26 | const selectRowUpdate = stub();
27 | const toggleExpandRow = () => {};
28 |
29 | const mountWrapper = mount(
30 |
31 |
38 | some content
39 |
40 | ,
41 | );
42 |
43 | const props = mountWrapper.find(TableCell).props();
44 | const classNames = props.className.split(' ');
45 | const finalClass = classNames[classNames.length - 1];
46 |
47 | assert.strictEqual(props.myProp, 'test');
48 | assert.strictEqual(finalClass, 'testClass');
49 | });
50 |
51 | it('should render a table head cell with sort label when options.sort = true provided', () => {
52 | const options = { sort: true, textLabels: getTextLabels() };
53 | const toggleSort = () => {};
54 |
55 | const wrapper = mount(
56 |
57 |
58 | some content
59 |
60 | ,
61 | );
62 |
63 | const actualResult = wrapper.find(TableSortLabel);
64 | assert.strictEqual(actualResult.length, 1);
65 | });
66 |
67 | it('should render a table head cell without sort label when options.sort = false provided', () => {
68 | const options = { sort: false, textLabels: getTextLabels() };
69 | const toggleSort = () => {};
70 |
71 | const shallowWrapper = shallow(
72 |
73 |
74 | some content
75 |
76 | ,
77 | );
78 |
79 | const actualResult = shallowWrapper.find(TableSortLabel);
80 | assert.strictEqual(actualResult.length, 0);
81 | });
82 |
83 | it('should render a table help icon when hint provided', () => {
84 | const options = { sort: true, textLabels: getTextLabels() };
85 |
86 | const wrapper = mount(
87 |
88 |
89 | some content
90 |
91 | ,
92 | );
93 |
94 | const actualResult = wrapper.find(HelpIcon);
95 | assert.strictEqual(actualResult.length, 1);
96 | });
97 |
98 | it('should render a table head cell without custom tooltip when hint provided', () => {
99 | const options = { sort: true, textLabels: getTextLabels() };
100 |
101 | const shallowWrapper = shallow(
102 |
103 |
104 | some content
105 |
106 | ,
107 | ).dive();
108 |
109 | const actualResult = shallowWrapper.find(HelpIcon);
110 | assert.strictEqual(actualResult.length, 0);
111 | });
112 |
113 | it('should trigger toggleSort prop callback when calling method handleSortClick', () => {
114 | const options = { sort: true, textLabels: getTextLabels() };
115 | const toggleSort = spy();
116 |
117 | const wrapper = mount(
118 |
119 |
126 | some content
127 |
128 | ,
129 | );
130 |
131 | const instance = wrapper
132 | .find('td')
133 | .at(0)
134 | .childAt(0);
135 | const event = { target: { value: 'All' } };
136 | instance.simulate('click');
137 | assert.strictEqual(toggleSort.callCount, 1);
138 | });
139 | });
140 |
--------------------------------------------------------------------------------
/test/MUIDataTablePagination.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { spy } from 'sinon';
3 | import { mount, shallow } from 'enzyme';
4 | import { assert } from 'chai';
5 | import MuiTablePagination from '@mui/material/TablePagination';
6 | import getTextLabels from '../src/textLabels';
7 | import TablePagination from '../src/components/TablePagination';
8 |
9 | describe(' ', function() {
10 | let options;
11 |
12 | before(() => {
13 | options = {
14 | rowsPerPageOptions: [5, 10, 15],
15 | textLabels: getTextLabels(),
16 | };
17 | });
18 |
19 | it('should render a table footer with pagination', () => {
20 | const mountWrapper = mount( );
21 |
22 | const actualResult = mountWrapper.find(MuiTablePagination);
23 | assert.strictEqual(actualResult.length, 1);
24 | });
25 |
26 | it('should trigger changePage prop callback when page is changed', () => {
27 | const changePage = spy();
28 | const wrapper = mount(
29 | ,
30 | );
31 |
32 | wrapper
33 | .find('#pagination-next')
34 | .at(0)
35 | .simulate('click');
36 | wrapper.unmount();
37 |
38 | assert.strictEqual(changePage.callCount, 1);
39 | });
40 |
41 | it('should correctly change page to be in bounds if out of bounds page was set', () => {
42 | // Set a page that is too high for the count and rowsPerPage
43 | const mountWrapper = mount( );
44 | const actualResult = mountWrapper.find(MuiTablePagination).props().page;
45 |
46 | // material-ui v3 does some internal calculations to protect against out of bounds pages, but material v4 does not
47 | assert.strictEqual(actualResult, 0);
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/test/MUIDataTableSearch.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import simulant from 'simulant';
3 | import { spy, stub } from 'sinon';
4 | import { mount, shallow } from 'enzyme';
5 | import { assert, expect, should } from 'chai';
6 | import TextField from '@mui/material/TextField';
7 | import TableSearch from '../src/components/TableSearch';
8 | import getTextLabels from '../src/textLabels';
9 |
10 | describe(' ', function() {
11 | it('should render a search bar', () => {
12 | const options = { textLabels: getTextLabels() };
13 | const onSearch = () => {};
14 | const onHide = () => {};
15 |
16 | const mountWrapper = mount( );
17 |
18 | const actualResult = mountWrapper.find(TextField);
19 | assert.strictEqual(actualResult.length, 1);
20 | });
21 |
22 | it('should render a search bar with text initialized', () => {
23 | const options = { textLabels: getTextLabels() };
24 | const onSearch = () => {};
25 | const onHide = () => {};
26 |
27 | const mountWrapper = mount(
28 | ,
29 | );
30 | const actualResult = mountWrapper.find(TextField);
31 | assert.strictEqual(actualResult.length, 1);
32 | assert.strictEqual(actualResult.props().value, 'searchText');
33 | });
34 |
35 | it('should change search bar text when searchText changes', () => {
36 | const options = { textLabels: getTextLabels() };
37 | const onSearch = () => {};
38 | const onHide = () => {};
39 |
40 | const mountWrapper = mount(
41 | ,
42 | );
43 | const actualResult = mountWrapper.setProps({ searchText: 'nextText' }).update();
44 | assert.strictEqual(actualResult.length, 1);
45 | assert.strictEqual(actualResult.find(TextField).props().value, 'nextText');
46 | });
47 |
48 | it('should render a search bar with placeholder when searchPlaceholder is set', () => {
49 | const options = { textLabels: getTextLabels(), searchPlaceholder: 'TestingPlaceholder' };
50 | const onSearch = () => {};
51 | const onHide = () => {};
52 |
53 | const mountWrapper = mount( );
54 | const actualResult = mountWrapper.find(TextField);
55 | assert.strictEqual(actualResult.length, 1);
56 | assert.strictEqual(actualResult.props().placeholder, 'TestingPlaceholder');
57 | });
58 |
59 | it('should trigger handleTextChange prop callback when calling method handleTextChange', () => {
60 | const options = { onSearchChange: () => true, textLabels: getTextLabels() };
61 | const onSearch = spy();
62 | const onHide = () => {};
63 |
64 | const wrapper = mount( );
65 |
66 | wrapper
67 | .find('input')
68 | .at(0)
69 | .simulate('change', { target: { value: '' } });
70 | wrapper.unmount();
71 |
72 | assert.strictEqual(onSearch.callCount, 1);
73 | });
74 |
75 | it('should hide the search bar when hitting the ESCAPE key', () => {
76 | const options = { textLabels: getTextLabels() };
77 | const onHide = spy();
78 |
79 | const mountWrapper = mount( , { attachTo: document.body });
80 |
81 | simulant.fire(document.body.querySelector('input'), 'keydown', { keyCode: 27 });
82 | assert.strictEqual(onHide.callCount, 1);
83 | });
84 |
85 | it('should hide not hide search bar when entering anything but the ESCAPE key', () => {
86 | const options = { textLabels: getTextLabels() };
87 | const onHide = spy();
88 |
89 | const mountWrapper = mount( , { attachTo: document.body });
90 |
91 | simulant.fire(document.body.querySelector('input'), 'keydown', { keyCode: 25 });
92 | assert.strictEqual(onHide.callCount, 0);
93 | });
94 | });
95 |
--------------------------------------------------------------------------------
/test/MUIDataTableSelectCell.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { spy, stub } from 'sinon';
3 | import { mount, shallow } from 'enzyme';
4 | import { assert, expect, should } from 'chai';
5 | import Checkbox from '@mui/material/Checkbox';
6 | import TableSelectCell from '../src/components/TableSelectCell';
7 |
8 | describe(' ', function() {
9 | before(() => {});
10 |
11 | it('should render table select cell', () => {
12 | const mountWrapper = mount( );
13 |
14 | const actualResult = mountWrapper.find(Checkbox);
15 | assert.strictEqual(actualResult.length, 1);
16 | });
17 |
18 | it('should render table select cell checked', () => {
19 | const mountWrapper = mount( );
20 |
21 | const actualResult = mountWrapper.find(Checkbox);
22 | assert.strictEqual(actualResult.props().checked, true);
23 | });
24 |
25 | it('should render table select cell unchecked', () => {
26 | const mountWrapper = mount( );
27 |
28 | const actualResult = mountWrapper.find(Checkbox);
29 | assert.strictEqual(actualResult.props().checked, false);
30 | });
31 |
32 | // it("should trigger onColumnUpdate prop callback when calling method handleColChange", () => {
33 | // const options = {};
34 | // const onColumnUpdate = spy();
35 |
36 | // const shallowWrapper = shallow(
37 | // ,
43 | // ).dive();
44 |
45 | // const instance = shallowWrapper.instance();
46 |
47 | // instance.handleColChange(0);
48 | // assert.strictEqual(onColumnUpdate.callCount, 1);
49 | // });
50 | });
51 |
--------------------------------------------------------------------------------
/test/MUIDataTableToolbarCustomIcons.test.js:
--------------------------------------------------------------------------------
1 | import IconButton from '@mui/material/IconButton';
2 | import DownloadIcon from '@mui/icons-material/CloudDownload';
3 | import FilterIcon from '@mui/icons-material/FilterList';
4 | import PrintIcon from '@mui/icons-material/Print';
5 | import SearchIcon from '@mui/icons-material/Search';
6 | import ViewColumnIcon from '@mui/icons-material/ViewColumn';
7 | import Chip from '@mui/material/Chip';
8 | import { assert } from 'chai';
9 | import { mount } from 'enzyme';
10 | import React from 'react';
11 | import TableToolbar from '../src/components/TableToolbar';
12 | import getTextLabels from '../src/textLabels';
13 |
14 | const CustomChip = props => {
15 | return ;
16 | };
17 |
18 | const icons = {
19 | SearchIcon,
20 | DownloadIcon,
21 | PrintIcon,
22 | ViewColumnIcon,
23 | FilterIcon,
24 | };
25 | let setTableAction = () => {};
26 | const options = {
27 | print: true,
28 | download: true,
29 | search: true,
30 | filter: true,
31 | viewColumns: true,
32 | textLabels: getTextLabels(),
33 | downloadOptions: {
34 | separator: ',',
35 | filename: 'tableDownload.csv',
36 | filterOptions: {
37 | useDisplayedRowsOnly: true,
38 | useDisplayedColumnsOnly: true,
39 | },
40 | },
41 | };
42 | const columns = ['First Name', 'Company', 'City', 'State'];
43 | const data = [
44 | {
45 | data: ['Joe James', 'Test Corp', 'Yonkers', 'NY'],
46 | dataIndex: 0,
47 | },
48 | {
49 | data: ['John Walsh', 'Test Corp', 'Hartford', 'CT'],
50 | dataIndex: 1,
51 | },
52 | {
53 | data: ['Bob Herm', 'Test Corp', 'Tampa', 'FL'],
54 | dataIndex: 2,
55 | },
56 | {
57 | data: ['James Houston', 'Test Corp', 'Dallas', 'TX'],
58 | dataIndex: 3,
59 | },
60 | ];
61 |
62 | const testCustomIcon = iconName => {
63 | const components = { icons: { [iconName]: CustomChip } };
64 | const wrapper = mount( );
65 | assert.strictEqual(wrapper.find(IconButton).length, 5); // All icons show
66 | assert.strictEqual(wrapper.find(CustomChip).length, 1); // Custom chip shows once
67 | Object.keys(icons).forEach(icon => {
68 | // The original default for the custom icon should be gone, the rest should remain
69 | assert.strictEqual(wrapper.find(icons[icon]).length, iconName === icon ? 0 : 1);
70 | });
71 | };
72 |
73 | describe(' with custom icons', function() {
74 | it('should render a toolbar with a custom chip in place of the search icon', () => {
75 | testCustomIcon('SearchIcon');
76 | });
77 |
78 | it('should render a toolbar with a custom chip in place of the download icon', () => {
79 | testCustomIcon('DownloadIcon');
80 | });
81 |
82 | it('should render a toolbar with a custom chip in place of the print icon', () => {
83 | testCustomIcon('PrintIcon');
84 | });
85 |
86 | it('should render a toolbar with a custom chip in place of the view columns icon', () => {
87 | testCustomIcon('ViewColumnIcon');
88 | });
89 |
90 | it('should render a toolbar with a custom chip in place of the filter icon', () => {
91 | testCustomIcon('FilterIcon');
92 | });
93 | });
94 |
--------------------------------------------------------------------------------
/test/MUIDataTableToolbarSelect.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { match, spy, stub } from 'sinon';
3 | import { mount, shallow } from 'enzyme';
4 | import { assert, expect, should } from 'chai';
5 | import DeleteIcon from '@mui/icons-material/Delete';
6 | import TableToolbarSelect from '../src/components/TableToolbarSelect';
7 | import getTextLabels from '../src/textLabels';
8 |
9 | describe(' ', function() {
10 | before(() => {});
11 |
12 | it('should render table toolbar select', () => {
13 | const onRowsDelete = () => {};
14 | const mountWrapper = mount(
15 | ,
20 | );
21 |
22 | const actualResult = mountWrapper.find(DeleteIcon);
23 | assert.strictEqual(actualResult.length, 1);
24 | });
25 |
26 | it('should call customToolbarSelect with 3 arguments', () => {
27 | const onRowsDelete = () => {};
28 | const customToolbarSelect = spy();
29 | const selectedRows = { data: [1] };
30 | const displayData = [1];
31 |
32 | const mountWrapper = mount(
33 | ,
39 | );
40 |
41 | assert.strictEqual(customToolbarSelect.calledWith(selectedRows, displayData, match.typeOf('function')), true);
42 | });
43 |
44 | it('should throw TypeError if selectedRows is not an array of numbers', done => {
45 | const onRowsDelete = () => {};
46 | const selectRowUpdate = () => {};
47 | const customToolbarSelect = (_, __, setSelectedRows) => {
48 | const spySetSelectedRows = spy(setSelectedRows);
49 | try {
50 | spySetSelectedRows('');
51 | } catch (error) {
52 | //do nothing
53 | }
54 | try {
55 | spySetSelectedRows(['1']);
56 | } catch (error) {
57 | //do nothing
58 | }
59 |
60 | spySetSelectedRows.exceptions.forEach(error => assert.strictEqual(error instanceof TypeError, true));
61 |
62 | done();
63 | };
64 | const selectedRows = { data: [1] };
65 | const displayData = [1];
66 |
67 | const mountWrapper = mount(
68 | ,
75 | );
76 | });
77 |
78 | it('should call selectRowUpdate when customToolbarSelect passed and setSelectedRows was called', () => {
79 | const onRowsDelete = () => {};
80 | const selectRowUpdate = spy();
81 | const customToolbarSelect = (_, __, setSelectedRows) => {
82 | setSelectedRows([1]);
83 | };
84 | const selectedRows = { data: [1] };
85 | const displayData = [1];
86 |
87 | const mountWrapper = mount(
88 | ,
95 | );
96 |
97 | assert.strictEqual(selectRowUpdate.calledOnce, true);
98 | });
99 |
100 | it('should throw an error when multiple rows are selected and selectableRows="single"', () => {
101 | const onRowsDelete = () => {};
102 | const selectRowUpdate = spy();
103 | const selectedRows = { data: [1] };
104 | const displayData = [1];
105 | const catchErr = spy();
106 |
107 | const wrapper = shallow(
108 | ,
115 | );
116 | const instance = wrapper.dive().instance();
117 |
118 | try {
119 | instance.handleCustomSelectedRow([1, 2]);
120 | } catch (err) {
121 | catchErr();
122 | }
123 |
124 | assert.strictEqual(catchErr.calledOnce, true);
125 | });
126 | });
127 |
--------------------------------------------------------------------------------
/test/MUIDataTableViewCol.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { spy, stub } from 'sinon';
3 | import { mount, shallow } from 'enzyme';
4 | import { assert, expect, should } from 'chai';
5 | import Checkbox from '@mui/material/Checkbox';
6 | import TableViewCol from '../src/components/TableViewCol';
7 | import getTextLabels from '../src/textLabels';
8 | import { FormControlLabel } from '@mui/material';
9 |
10 | describe(' ', function() {
11 | let columns;
12 | let options;
13 |
14 | before(() => {
15 | columns = [
16 | { name: 'a', label: 'A', display: 'true' },
17 | { name: 'b', label: 'B', display: 'true' },
18 | { name: 'c', label: 'C', display: 'true' },
19 | { name: 'd', label: 'D', display: 'true' },
20 | ];
21 | options = {
22 | textLabels: getTextLabels(),
23 | };
24 | });
25 |
26 | it('should render view columns', () => {
27 | const mountWrapper = mount( );
28 |
29 | const actualResult = mountWrapper.find(Checkbox);
30 | assert.strictEqual(actualResult.length, 4);
31 | });
32 |
33 | it('should labels as view column names when present', () => {
34 | const mountWrapper = mount( );
35 | const labels = mountWrapper.find(FormControlLabel).map(n => n.text());
36 | assert.deepEqual(labels, ['A', 'B', 'C', 'D']);
37 | });
38 |
39 | it('should trigger onColumnUpdate prop callback when calling method handleColChange', () => {
40 | const onColumnUpdate = spy();
41 |
42 | const wrapper = mount( );
43 |
44 | wrapper
45 | .find('input[type="checkbox"]')
46 | .at(0)
47 | .simulate('change', { target: { checked: false, value: false } });
48 | wrapper.unmount();
49 |
50 | assert.strictEqual(onColumnUpdate.callCount, 1);
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/test/TableResize.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { spy, stub } from 'sinon';
3 | import { mount, shallow } from 'enzyme';
4 | import { assert, expect, should } from 'chai';
5 | import TableResize from '../src/components/TableResize';
6 | import MUIDataTable from '../src/MUIDataTable';
7 |
8 | describe(' ', function() {
9 | let options;
10 |
11 | before(() => {
12 | options = {
13 | resizableColumns: true,
14 | tableBodyHeight: '500px',
15 | };
16 | });
17 |
18 | it('should render a table resize component', () => {
19 | const updateDividers = spy();
20 | const setResizeable = spy();
21 |
22 | const mountWrapper = mount(
23 | ,
24 | );
25 |
26 | const actualResult = mountWrapper.find(TableResize);
27 | assert.strictEqual(actualResult.length, 1);
28 |
29 | assert.strictEqual(updateDividers.callCount, 1);
30 | assert.strictEqual(setResizeable.callCount, 1);
31 | });
32 |
33 | it('should create a coordinate map for each column', () => {
34 | const columns = ['Name', 'Age', 'Location', 'Phone'];
35 | const data = [['Joe', 26, 'Chile', '555-5555']];
36 |
37 | const shallowWrapper = mount( );
38 |
39 | var state = shallowWrapper
40 | .find(TableResize)
41 | .childAt(0)
42 | .state();
43 |
44 | var colCoordCount = 0;
45 | for (let prop in state.resizeCoords) {
46 | colCoordCount++;
47 | }
48 |
49 | shallowWrapper.unmount();
50 |
51 | assert.strictEqual(colCoordCount, 5);
52 | });
53 |
54 | it('should execute resize methods correctly', () => {
55 | const updateDividers = spy();
56 | let cellsRef = {
57 | 0: {
58 | left: 0,
59 | width: 50,
60 | getBoundingClientRect: () => ({
61 | left: 0,
62 | width: 50,
63 | height: 100,
64 | }),
65 | style: {},
66 | },
67 | 1: {
68 | left: 50,
69 | width: 50,
70 | getBoundingClientRect: () => ({
71 | left: 50,
72 | width: 50,
73 | height: 100,
74 | }),
75 | style: {},
76 | },
77 | };
78 | let tableRef = {
79 | style: {
80 | width: '100px',
81 | },
82 | getBoundingClientRect: () => ({
83 | width: 100,
84 | height: 100,
85 | }),
86 | offsetParent: {
87 | offsetLeft: 0,
88 | },
89 | };
90 |
91 | const setResizeable = next => {
92 | next(cellsRef, tableRef);
93 | };
94 |
95 | const shallowWrapper = shallow(
96 | ,
97 | );
98 | const instance = shallowWrapper.dive().instance();
99 |
100 | instance.handleResize();
101 |
102 | let evt = {
103 | clientX: 48,
104 | };
105 | instance.onResizeStart(0, evt);
106 | instance.onResizeMove(0, evt);
107 | instance.onResizeEnd(0, evt);
108 |
109 | evt = {
110 | clientX: 52,
111 | };
112 | instance.onResizeStart(0, evt);
113 | instance.onResizeMove(0, evt);
114 | instance.onResizeEnd(0, evt);
115 |
116 | let endState = shallowWrapper.dive().state();
117 | //console.dir(endState);
118 |
119 | assert.strictEqual(endState.tableWidth, 100);
120 | assert.strictEqual(endState.tableHeight, 100);
121 | });
122 | });
123 |
--------------------------------------------------------------------------------
/test/UseColumnDrop.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { spy, stub } from 'sinon';
3 | import { mount, shallow } from 'enzyme';
4 | import { assert, expect, should } from 'chai';
5 | import { getColModel, reorderColumns, handleHover } from '../src/hooks/useColumnDrop';
6 |
7 | describe('useColumnDrop', function() {
8 | before(() => {});
9 |
10 | it('should reorder columns when reorderColumns is called', () => {
11 | let prevColumnOrder = [1, 2, 3, 4];
12 | let newOrder = reorderColumns(prevColumnOrder, 1, 4);
13 |
14 | expect(newOrder).to.eql([2, 3, 4, 1]);
15 | });
16 |
17 | it('should build a column model object when getColModel is called', () => {
18 | let offsetParent = {
19 | offsetLeft: 10,
20 | offsetParent: null,
21 | };
22 | let headCellRefs = {
23 | 0: {
24 | offsetLeft: 0,
25 | offsetParent: 0,
26 | offsetWidth: 0,
27 | offsetParent: offsetParent,
28 | },
29 | 1: {
30 | offsetLeft: 0,
31 | offsetParent: 0,
32 | offsetWidth: 0,
33 | offsetParent: null,
34 | },
35 | 2: {
36 | offsetLeft: 0,
37 | offsetParent: 0,
38 | offsetWidth: 0,
39 | offsetParent: null,
40 | },
41 | };
42 | let columnOrder = [0, 1];
43 | let columns = [
44 | {
45 | display: 'true',
46 | },
47 | {
48 | display: 'true',
49 | },
50 | ];
51 |
52 | let newModel = getColModel(headCellRefs, columnOrder, columns);
53 |
54 | expect(newModel.length).to.equal(3);
55 | expect(newModel[0].left).to.equal(10);
56 | expect(newModel[0].ref.offsetParent).to.equal(offsetParent);
57 | expect(newModel[1].columnIndex).to.equal(0);
58 | });
59 |
60 | it('should build a column model object when getColModel is called and no select cell exists', () => {
61 | let headCellRefs = {
62 | 0: null,
63 | 1: {
64 | offsetLeft: 0,
65 | offsetParent: 0,
66 | offsetWidth: 0,
67 | offsetParent: null,
68 | },
69 | 2: {
70 | offsetLeft: 0,
71 | offsetParent: 0,
72 | offsetWidth: 0,
73 | offsetParent: null,
74 | },
75 | };
76 | let columnOrder = [0, 1];
77 | let columns = [
78 | {
79 | display: 'true',
80 | },
81 | {
82 | display: 'true',
83 | },
84 | ];
85 |
86 | let newModel = getColModel(headCellRefs, columnOrder, columns);
87 |
88 | expect(newModel.length).to.equal(2);
89 | expect(newModel[0].left).to.equal(0);
90 | expect(newModel[1].columnIndex).to.equal(1);
91 | });
92 |
93 | it('should set columnShift on timers when handleHover is called', () => {
94 | let offsetParent = {
95 | offsetLeft: 10,
96 | offsetParent: null,
97 | };
98 | let headCellRefs = {
99 | 0: {
100 | offsetLeft: 0,
101 | offsetParent: 0,
102 | offsetWidth: 0,
103 | offsetParent: offsetParent,
104 | style: {},
105 | },
106 | 1: {
107 | offsetLeft: 0,
108 | offsetParent: 0,
109 | offsetWidth: 10,
110 | offsetParent: null,
111 | style: {},
112 | },
113 | 2: {
114 | offsetLeft: 0,
115 | offsetParent: 0,
116 | offsetWidth: 10,
117 | offsetParent: null,
118 | style: {},
119 | },
120 | };
121 | let columnOrder = [0, 1];
122 | let columns = [
123 | {
124 | display: 'true',
125 | },
126 | {
127 | display: 'true',
128 | },
129 | ];
130 | let timers = {
131 | columnShift: null,
132 | };
133 |
134 | handleHover({
135 | item: {
136 | columnIndex: 0,
137 | left: 0,
138 | style: {},
139 | },
140 | mon: {
141 | getItem: () => ({
142 | colIndex: 1,
143 | headCellRefs: headCellRefs,
144 | }),
145 | getClientOffset: () => ({
146 | x: 15,
147 | }),
148 | },
149 | index: 0,
150 | headCellRefs,
151 | updateColumnOrder: spy(),
152 | columnOrder: [0, 1],
153 | transitionTime: 0,
154 | tableRef: {
155 | querySelectorAll: () => [
156 | {
157 | style: {},
158 | },
159 | ],
160 | },
161 | tableId: '123',
162 | timers,
163 | columns,
164 | });
165 |
166 | expect(timers.columnShift).to.not.equal(null);
167 | });
168 | });
169 |
--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --require ./test/setup-mocha-env.js
2 | --extensions js,jsx
3 |
--------------------------------------------------------------------------------
/test/setup-mocha-env.js:
--------------------------------------------------------------------------------
1 | import Enzyme from 'enzyme';
2 | import React from 'react';
3 | import Adapter from 'enzyme-adapter-react-16';
4 |
5 | /* required when running >= 16.0 */
6 | Enzyme.configure({ adapter: new Adapter() });
7 |
8 | function setupDom() {
9 | const { JSDOM } = require('jsdom');
10 | const Node = require('jsdom/lib/jsdom/living/node-document-position');
11 |
12 | const dom = new JSDOM('');
13 |
14 | global.window = dom.window;
15 | global.document = window.document;
16 | global.Node = Node;
17 |
18 | global.navigator = {
19 | userAgent: 'node.js',
20 | appVersion: '',
21 | };
22 |
23 | function copyProps(src, target) {
24 | const props = Object.getOwnPropertyNames(src)
25 | .filter(prop => typeof target[prop] === 'undefined')
26 | .map(prop => Object.getOwnPropertyDescriptor(src, prop));
27 | Object.defineProperties(target, props);
28 | }
29 |
30 | copyProps(dom.window, global);
31 |
32 | const KEYS = ['HTMLElement'];
33 | KEYS.forEach(key => {
34 | global[key] = window[key];
35 | });
36 |
37 | global.document.createRange = () => ({
38 | setStart: () => {},
39 | setEnd: () => {},
40 | commonAncestorContainer: {
41 | nodeName: 'BODY',
42 | ownerDocument: {
43 | documentElement: window.document.body,
44 | parent: {
45 | nodeName: 'BODY',
46 | },
47 | },
48 | },
49 | });
50 |
51 | global.requestAnimationFrame = callback => {
52 | setTimeout(callback, 0);
53 | };
54 |
55 | global.window.cancelAnimationFrame = () => {};
56 | global.getComputedStyle = global.window.getComputedStyle;
57 | global.HTMLInputElement = global.window.HTMLInputElement;
58 | global.Element = global.window.Element;
59 | global.Event = global.window.Event;
60 | global.dispatchEvent = global.window.dispatchEvent;
61 | global.window.getComputedStyle = () => ({});
62 |
63 | Object.defineProperty(global.window.URL, 'createObjectURL', { value: () => {} });
64 | global.Blob = () => '';
65 | }
66 |
67 | setupDom();
68 | console.error = function() {};
69 |
--------------------------------------------------------------------------------
/test/utils.test.js:
--------------------------------------------------------------------------------
1 | import { getPageValue, buildCSV, createCSVDownload, escapeDangerousCSVCharacters } from '../src/utils';
2 | import { spy } from 'sinon';
3 | import { assert } from 'chai';
4 |
5 | describe('utils.js', () => {
6 | describe('escapeDangerousCSVCharacters', () => {
7 | it('properly escapes the first character in a string if it can be used for injection', () => {
8 | assert.strictEqual(escapeDangerousCSVCharacters('+SUM(1+1)'), "'+SUM(1+1)");
9 | assert.strictEqual(escapeDangerousCSVCharacters('-SUM(1+1)'), "'-SUM(1+1)");
10 | assert.strictEqual(escapeDangerousCSVCharacters('=SUM(1+1)'), "'=SUM(1+1)");
11 | assert.strictEqual(escapeDangerousCSVCharacters('@SUM(1+1)'), "'@SUM(1+1)");
12 | assert.equal(escapeDangerousCSVCharacters(123), 123);
13 | });
14 | });
15 |
16 | describe('getPageValue', () => {
17 | it('returns the highest in bounds page value when page is out of bounds and count is greater than rowsPerPage', () => {
18 | const count = 30;
19 | const rowsPerPage = 10;
20 | const page = 5;
21 |
22 | const actualResult = getPageValue(count, rowsPerPage, page);
23 | assert.strictEqual(actualResult, 2);
24 | });
25 |
26 | it('returns the highest in bounds page value when page is in bounds and count is greater than rowsPerPage', () => {
27 | const count = 30;
28 | const rowsPerPage = 10;
29 | const page = 1;
30 |
31 | const actualResult = getPageValue(count, rowsPerPage, page);
32 | assert.strictEqual(actualResult, 1);
33 | });
34 |
35 | it('returns the highest in bounds page value when page is out of bounds and count is less than rowsPerPage', () => {
36 | const count = 3;
37 | const rowsPerPage = 10;
38 | const page = 1;
39 |
40 | const actualResult = getPageValue(count, rowsPerPage, page);
41 | assert.strictEqual(actualResult, 0);
42 | });
43 |
44 | it('returns the highest in bounds page value when page is in bounds and count is less than rowsPerPage', () => {
45 | const count = 3;
46 | const rowsPerPage = 10;
47 | const page = 0;
48 |
49 | const actualResult = getPageValue(count, rowsPerPage, page);
50 | assert.strictEqual(actualResult, 0);
51 | });
52 |
53 | it('returns the highest in bounds page value when page is out of bounds and count is equal to rowsPerPage', () => {
54 | const count = 10;
55 | const rowsPerPage = 10;
56 | const page = 1;
57 |
58 | const actualResult = getPageValue(count, rowsPerPage, page);
59 | assert.strictEqual(actualResult, 0);
60 | });
61 |
62 | it('returns the highest in bounds page value when page is in bounds and count is equal to rowsPerPage', () => {
63 | const count = 10;
64 | const rowsPerPage = 10;
65 | const page = 0;
66 |
67 | const actualResult = getPageValue(count, rowsPerPage, page);
68 | assert.strictEqual(actualResult, 0);
69 | });
70 | });
71 |
72 | describe('buildCSV', () => {
73 | const options = {
74 | downloadOptions: {
75 | separator: ';',
76 | },
77 | onDownload: null,
78 | };
79 | const columns = [
80 | {
81 | name: 'firstname',
82 | download: true,
83 | },
84 | {
85 | name: 'lastname',
86 | download: true,
87 | },
88 | ];
89 |
90 | it('properly builds a csv when given a non-empty dataset', () => {
91 | const data = [{ data: ['anton', 'abraham'] }, { data: ['berta', 'buchel'] }];
92 | const csv = buildCSV(columns, data, options);
93 |
94 | assert.strictEqual(csv, '"firstname";"lastname"\r\n' + '"anton";"abraham"\r\n' + '"berta";"buchel"');
95 | });
96 |
97 | it('returns an empty csv with header when given an empty dataset', () => {
98 | const data = [];
99 | const csv = buildCSV(columns, data, options);
100 |
101 | assert.strictEqual(csv, '"firstname";"lastname"');
102 | });
103 | });
104 |
105 | describe('createCSVDownload', () => {
106 | const columns = [
107 | {
108 | name: 'firstname',
109 | download: true,
110 | },
111 | {
112 | name: 'lastname',
113 | download: true,
114 | },
115 | ];
116 | const data = [{ data: ['anton', 'abraham'] }, { data: ['berta', 'buchel'] }];
117 |
118 | it('does not call download function if download callback returns `false`', () => {
119 | const options = {
120 | downloadOptions: {
121 | separator: ';',
122 | },
123 | onDownload: () => false,
124 | };
125 | const downloadCSV = spy();
126 |
127 | createCSVDownload(columns, data, options, downloadCSV);
128 |
129 | assert.strictEqual(downloadCSV.callCount, 0);
130 | });
131 |
132 | it('calls download function if download callback returns truthy', () => {
133 | const options = {
134 | downloadOptions: {
135 | separator: ';',
136 | },
137 | onDownload: () => true,
138 | };
139 | const downloadCSV = spy();
140 |
141 | createCSVDownload(columns, data, options, downloadCSV);
142 |
143 | assert.strictEqual(downloadCSV.callCount, 1);
144 | });
145 | });
146 | });
147 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 |
3 | module.exports = {
4 | entry: {
5 | app: ['core-js/stable', 'regenerator-runtime/runtime', './examples/Router/index.js'],
6 | },
7 | stats: 'verbose',
8 | context: __dirname,
9 | output: {
10 | filename: 'bundle.js',
11 | },
12 | devtool: 'source-map',
13 | devServer: {
14 | disableHostCheck: true,
15 | host: 'localhost',
16 | hot: true,
17 | inline: true,
18 | port: 5050,
19 | stats: 'errors-warnings',
20 | },
21 | module: {
22 | rules: [
23 | {
24 | test: /\.(js|jsx)$/,
25 | exclude: /(node_modules)/,
26 | use: ['babel-loader', 'eslint-loader'],
27 | },
28 | {
29 | test: /\.css$/i,
30 | use: ['style-loader', 'css-loader'],
31 | },
32 | ],
33 | },
34 | plugins: [
35 | new webpack.HotModuleReplacementPlugin(),
36 | new webpack.NamedModulesPlugin(),
37 | new webpack.DefinePlugin({
38 | 'process.env': {
39 | NODE_ENV: JSON.stringify('development'),
40 | },
41 | }),
42 | ],
43 | };
44 |
--------------------------------------------------------------------------------