{options.search && (
(this.searchButton = el)}
classes={{ root: this.getActiveIcon(classes, 'search') }}
- onClick={this.setActiveIcon.bind(null, 'search')}>
+ onClick={this.handleSearchIconClick}>
diff --git a/src/components/TableToolbarSelect.js b/src/components/TableToolbarSelect.js
index 7f1bbe4cb..73f7e9656 100644
--- a/src/components/TableToolbarSelect.js
+++ b/src/components/TableToolbarSelect.js
@@ -75,7 +75,7 @@ class TableToolbarSelect extends React.Component {
) : (
-
+
)}
diff --git a/src/index.js b/src/index.js
index f71f87a46..bdf7ebece 100644
--- a/src/index.js
+++ b/src/index.js
@@ -16,3 +16,4 @@ export { default as TableSelectCell } from './components/TableSelectCell';
export { default as TableToolbar } from './components/TableToolbar';
export { default as TableToolbarSelect } from './components/TableToolbarSelect';
export { default as TableViewCol } from './components/TableViewCol';
+export { debounceSearchRender, DebounceTableSearch } from './plug-ins/DebounceSearchRender';
diff --git a/src/plug-ins/DebounceSearchRender.js b/src/plug-ins/DebounceSearchRender.js
new file mode 100644
index 000000000..25498789e
--- /dev/null
+++ b/src/plug-ins/DebounceSearchRender.js
@@ -0,0 +1,114 @@
+import React, { useEffect } from 'react';
+import Grow from '@material-ui/core/Grow';
+import TextField from '@material-ui/core/TextField';
+import SearchIcon from '@material-ui/icons/Search';
+import IconButton from '@material-ui/core/IconButton';
+import ClearIcon from '@material-ui/icons/Clear';
+import { withStyles } from '@material-ui/core/styles';
+
+function debounce(func, wait, immediate) {
+ var timeout;
+ return function() {
+ var context = this,
+ args = arguments;
+ var later = function() {
+ timeout = null;
+ if (!immediate) func.apply(context, args);
+ };
+ var callNow = immediate && !timeout;
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ if (callNow) func.apply(context, args);
+ };
+}
+
+const defaultStyles = theme => ({
+ main: {
+ display: 'flex',
+ flex: '1 0 auto',
+ },
+ searchIcon: {
+ color: theme.palette.text.secondary,
+ marginTop: '10px',
+ marginRight: '8px',
+ },
+ searchText: {
+ flex: '0.8 0',
+ },
+ clearIcon: {
+ '&:hover': {
+ color: theme.palette.error.main,
+ },
+ },
+});
+
+class _DebounceTableSearch extends React.Component {
+ handleTextChangeWrapper = debouncedSearch => {
+ return function(event) {
+ debouncedSearch(event.target.value);
+ };
+ };
+
+ componentDidMount() {
+ document.addEventListener('keydown', this.onKeyDown, false);
+ }
+
+ componentWillUnmount() {
+ document.removeEventListener('keydown', this.onKeyDown, false);
+ }
+
+ onKeyDown = event => {
+ if (event.keyCode === 27) {
+ this.props.onHide();
+ }
+ };
+
+ render() {
+ const { classes, options, onHide, searchText, debounceWait } = this.props;
+
+ const debouncedSearch = debounce(value => {
+ this.props.onSearch(value);
+ }, debounceWait);
+
+ return (
+
+
+
+ (this.searchField = el)}
+ placeholder={options.searchPlaceholder}
+ />
+
+
+
+
+
+ );
+ }
+}
+
+var DebounceTableSearch = withStyles(defaultStyles, { name: 'MUIDataTableSearch' })(_DebounceTableSearch);
+export { DebounceTableSearch };
+
+export function debounceSearchRender(debounceWait = 200) {
+ return (searchText, handleSearch, hideSearch, options) => {
+ return (
+
+ );
+ };
+}
diff --git a/src/textLabels.js b/src/textLabels.js
index a2df615f0..54f9ca1d1 100644
--- a/src/textLabels.js
+++ b/src/textLabels.js
@@ -1,7 +1,7 @@
/*
* Default text labels.
*/
-const textLabels = {
+const getTextLabels = () => ({
body: {
noMatch: 'Sorry, no matching records found',
toolTip: 'Sort',
@@ -33,6 +33,6 @@ const textLabels = {
delete: 'Delete',
deleteAria: 'Delete Selected Rows',
},
-};
+});
-export default textLabels;
+export default getTextLabels;
diff --git a/src/utils.js b/src/utils.js
index 3eb2c4605..39773a34c 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -5,6 +5,22 @@ function buildMap(rows) {
}, {});
}
+function escapeDangerousCSVCharacters(data) {
+ if (typeof data === 'string') {
+ // Places single quote before the appearance of dangerous characters if they
+ // are the first in the data string.
+ return data.replace(/^\+|^\-|^\=|^\@/g, "'$&");
+ }
+
+ return data;
+}
+
+function warnDeprecated(warning) {
+ if (process.env.NODE_ENV === 'development') {
+ console.error(`Deprecation Notice: ${warning}`);
+ }
+}
+
function getPageValue(count, rowsPerPage, page) {
const totalPages = count <= rowsPerPage ? 1 : Math.ceil(count / rowsPerPage);
@@ -43,7 +59,11 @@ function buildCSV(columns, data, options) {
.reduce(
(soFar, column) =>
column.download
- ? soFar + '"' + replaceDoubleQuoteInString(column.name) + '"' + options.downloadOptions.separator
+ ? soFar +
+ '"' +
+ escapeDangerousCSVCharacters(replaceDoubleQuoteInString(column.label || column.name)) +
+ '"' +
+ options.downloadOptions.separator
: soFar,
'',
)
@@ -61,7 +81,7 @@ function buildCSV(columns, data, options) {
'"' +
row.data
.filter((_, index) => columns[index].download)
- .map(columnData => replaceDoubleQuoteInString(columnData))
+ .map(columnData => escapeDangerousCSVCharacters(replaceDoubleQuoteInString(columnData)))
.join('"' + options.downloadOptions.separator + '"') +
'"\r\n',
'',
@@ -108,4 +128,14 @@ function createCSVDownload(columns, data, options, downloadCSV) {
downloadCSV(csv, options.downloadOptions.filename);
}
-export { buildMap, getPageValue, getCollatorComparator, sortCompare, createCSVDownload, buildCSV, downloadCSV };
+export {
+ buildMap,
+ getPageValue,
+ getCollatorComparator,
+ sortCompare,
+ createCSVDownload,
+ buildCSV,
+ downloadCSV,
+ warnDeprecated,
+ escapeDangerousCSVCharacters,
+};
diff --git a/test/MUIDataTable.test.js b/test/MUIDataTable.test.js
index 53fc06557..86da41507 100644
--- a/test/MUIDataTable.test.js
+++ b/test/MUIDataTable.test.js
@@ -8,7 +8,7 @@ import TableFilterList from '../src/components/TableFilterList';
import TablePagination from '../src/components/TablePagination';
import TableToolbar from '../src/components/TableToolbar';
import TableToolbarSelect from '../src/components/TableToolbarSelect';
-import textLabels from '../src/textLabels';
+import getTextLabels from '../src/textLabels';
import Chip from '@material-ui/core/Chip';
import Cities from '../examples/component/cities';
import { getCollatorComparator } from '../src/utils';
@@ -512,7 +512,7 @@ describe('
', function() {
it('should correctly build internal rowsPerPage when provided in options', () => {
const options = {
rowsPerPage: 20,
- textLabels,
+ textLabels: getTextLabels(),
};
const shallowWrapper = shallow(
);
@@ -875,7 +875,7 @@ describe('
', function() {
it('should properly set searchText when hiding the search bar', () => {
const options = {
rowsPerPage: 1,
- textLabels,
+ textLabels: getTextLabels(),
};
const shallowWrapper = shallow(
);
const table = shallowWrapper.dive();
@@ -905,7 +905,7 @@ describe('
', function() {
it('should not change page when hiding the search bar', () => {
const options = {
rowsPerPage: 1,
- textLabels,
+ textLabels: getTextLabels(),
};
const shallowWrapper = shallow(
);
const table = shallowWrapper.dive();
@@ -1116,7 +1116,7 @@ describe('
', function() {
it('should recalculate page when calling changeRowsPerPage method', () => {
const mountWrapper = mount(
- shallow(
).get(0),
+ shallow(
).get(0),
);
const instance = mountWrapper.instance();
@@ -1179,7 +1179,10 @@ describe('
', function() {
instance.selectRowUpdate('cell', { index: 1, dataIndex: 1 });
shallowWrapper.update();
- const expectedResult = [{ index: 0, dataIndex: 0 }, { index: 1, dataIndex: 1 }];
+ const expectedResult = [
+ { index: 0, dataIndex: 0 },
+ { index: 1, dataIndex: 1 },
+ ];
const state = shallowWrapper.state();
assert.deepEqual(state.selectedRows.data, expectedResult);
});
@@ -1192,7 +1195,10 @@ describe('
', function() {
shallowWrapper.update();
const state = shallowWrapper.state();
- const expectedResult = [{ index: 0, dataIndex: 0 }, { index: 3, dataIndex: 3 }];
+ const expectedResult = [
+ { index: 0, dataIndex: 0 },
+ { index: 3, dataIndex: 3 },
+ ];
assert.deepEqual(state.selectedRows.data, expectedResult);
});
@@ -1208,7 +1214,10 @@ describe('
', function() {
]);
shallowWrapper.update();
- const expectedResult = [{ index: 0, dataIndex: 0 }, { index: 1, dataIndex: 1 }];
+ const expectedResult = [
+ { index: 0, dataIndex: 0 },
+ { index: 1, dataIndex: 1 },
+ ];
const state = shallowWrapper.state();
assert.deepEqual(state.selectedRows.data, expectedResult);
});
@@ -1267,7 +1276,10 @@ describe('
', function() {
const instance = shallowWrapper.instance();
const state = shallowWrapper.state();
- const expectedResult = [{ index: 0, dataIndex: 0 }, { index: 3, dataIndex: 3 }];
+ const expectedResult = [
+ { index: 0, dataIndex: 0 },
+ { index: 3, dataIndex: 3 },
+ ];
assert.deepEqual(state.selectedRows.data, expectedResult);
});
@@ -1280,7 +1292,10 @@ describe('
', function() {
const instance = shallowWrapper.instance();
const state = shallowWrapper.state();
- const expectedResult = [{ index: 0, dataIndex: 0 }, { index: 3, dataIndex: 3 }];
+ const expectedResult = [
+ { index: 0, dataIndex: 0 },
+ { index: 3, dataIndex: 3 },
+ ];
assert.deepEqual(state.selectedRows.data, expectedResult);
});
@@ -1299,7 +1314,10 @@ describe('
', function() {
const instance = shallowWrapper.instance();
const state = shallowWrapper.state();
- const expectedResult = [{ index: 0, dataIndex: 0 }, { index: 3, dataIndex: 3 }];
+ const expectedResult = [
+ { index: 0, dataIndex: 0 },
+ { index: 3, dataIndex: 3 },
+ ];
assert.deepEqual(state.expandedRows.data, expectedResult);
});
@@ -1445,7 +1463,10 @@ describe('
', function() {
shallowWrapper.update();
const state = shallowWrapper.state();
- const expectedResult = [{ index: 0, dataIndex: 0 }, { index: 3, dataIndex: 3 }];
+ const expectedResult = [
+ { index: 0, dataIndex: 0 },
+ { index: 3, dataIndex: 3 },
+ ];
assert.deepEqual(state.selectedRows.data, expectedResult);
assert.strictEqual(options.onTableChange.callCount, 1);
@@ -1666,7 +1687,12 @@ describe('
', function() {
},
},
];
- const data = [['other-data-1', 'a'], ['other-data-2', 'b'], ['other-data-3', 'c'], ['other-data-4', 'd']];
+ const data = [
+ ['other-data-1', 'a'],
+ ['other-data-2', 'b'],
+ ['other-data-3', 'c'],
+ ['other-data-4', 'd'],
+ ];
const options = {
filter: true,
filterType: 'dropdown',
diff --git a/test/MUIDataTableBody.test.js b/test/MUIDataTableBody.test.js
index 6afeb2365..054bca2b9 100644
--- a/test/MUIDataTableBody.test.js
+++ b/test/MUIDataTableBody.test.js
@@ -2,7 +2,7 @@ import React from 'react';
import { spy, stub } from 'sinon';
import { mount, shallow } from 'enzyme';
import { assert, expect, should } from 'chai';
-import textLabels from '../src/textLabels';
+import getTextLabels from '../src/textLabels';
import TableBody from '../src/components/TableBody';
import TableSelectCell from '../src/components/TableSelectCell';
import Checkbox from '@material-ui/core/Checkbox';
@@ -67,7 +67,7 @@ describe('
', function() {
});
it('should render a table body with no records if no data provided', () => {
- const options = { selectableRows: false, textLabels };
+ const options = { selectableRows: false, textLabels: getTextLabels() };
const selectRowUpdate = () => {};
const toggleExpandRow = () => {};
diff --git a/test/MUIDataTableFilter.test.js b/test/MUIDataTableFilter.test.js
index c5b3cb67d..510473817 100644
--- a/test/MUIDataTableFilter.test.js
+++ b/test/MUIDataTableFilter.test.js
@@ -7,7 +7,7 @@ import { mount, shallow } from 'enzyme';
import React from 'react';
import { spy } from 'sinon';
import TableFilter from '../src/components/TableFilter';
-import textLabels from '../src/textLabels';
+import getTextLabels from '../src/textLabels';
describe('
', function() {
let data;
@@ -38,7 +38,7 @@ describe('
', function() {
});
it('should render label as filter name', () => {
- const options = { filterType: 'checkbox', textLabels };
+ const options = { filterType: 'checkbox', textLabels: getTextLabels() };
const filterList = [[], [], [], []];
const shallowWrapper = mount(
,
@@ -51,7 +51,7 @@ describe('
', function() {
});
it("should render data table filter view with checkboxes if filterType = 'checkbox'", () => {
- const options = { filterType: 'checkbox', textLabels };
+ const options = { filterType: 'checkbox', textLabels: getTextLabels() };
const filterList = [[], [], [], []];
const shallowWrapper = mount(
,
@@ -62,7 +62,7 @@ describe('
', function() {
});
it('should render data table filter view with no checkboxes if filter=false for each column', () => {
- const options = { filterType: 'checkbox', textLabels };
+ const options = { filterType: 'checkbox', textLabels: getTextLabels() };
const filterList = [[], [], [], []];
columns = columns.map(item => (item.filter = false));
@@ -75,7 +75,7 @@ describe('
', function() {
});
it("should render data table filter view with selects if filterType = 'select'", () => {
- const options = { filterType: 'select', textLabels };
+ const options = { filterType: 'select', textLabels: getTextLabels() };
const filterList = [['Joe James'], [], [], []];
const mountWrapper = mount(
@@ -87,7 +87,7 @@ describe('
', function() {
});
it('should render data table filter view no selects if filter=false for each column', () => {
- const options = { filterType: 'select', textLabels };
+ const options = { filterType: 'select', textLabels: getTextLabels() };
const filterList = [['Joe James'], [], [], []];
columns = columns.map(item => (item.filter = false));
@@ -100,7 +100,7 @@ describe('
', function() {
});
it("should render data table filter view with checkbox selects if filterType = 'multiselect'", () => {
- const options = { filterType: 'multiselect', textLabels };
+ const options = { filterType: 'multiselect', textLabels: getTextLabels() };
const filterList = [['Joe James', 'John Walsh'], [], [], []];
const mountWrapper = mount(
@@ -114,7 +114,7 @@ describe('
', function() {
it("should data table custom filter view with if filterType = 'custom' and a valid display filterOption is provided", () => {
const options = {
filterType: 'custom',
- textLabels,
+ textLabels: getTextLabels(),
filterOptions: {
names: [],
logic(city, filters) {
@@ -137,7 +137,7 @@ describe('
', function() {
});
it("should render column.label as filter label if filterType = 'textField'", () => {
- const options = { filterType: 'textField', textLabels };
+ const options = { filterType: 'textField', textLabels: getTextLabels() };
const filterList = [[], [], [], []];
const shallowWrapper = mount(
,
@@ -150,7 +150,7 @@ describe('
', function() {
});
it("should data table filter view with TextFields if filterType = 'textfield'", () => {
- const options = { filterType: 'textField', textLabels };
+ const options = { filterType: 'textField', textLabels: getTextLabels() };
const filterList = [[], [], [], []];
const shallowWrapper = mount(
,
@@ -161,7 +161,7 @@ describe('
', function() {
});
it("should data table filter view with no TextFields if filter=false when filterType = 'textField'", () => {
- const options = { filterType: 'textField', textLabels };
+ const options = { filterType: 'textField', textLabels: getTextLabels() };
const filterList = [[], [], [], []];
columns = columns.map(item => (item.filter = false));
@@ -174,7 +174,7 @@ describe('
', function() {
});
it("should data table filter view with checkboxes if column.filterType = 'checkbox' irrespective of global filterType value", () => {
- const options = { filterType: 'textField', textLabels };
+ const options = { filterType: 'textField', textLabels: getTextLabels() };
const filterList = [[], [], [], []];
columns.forEach(item => (item.filterType = 'checkbox'));
@@ -188,7 +188,7 @@ describe('
', function() {
it('should render a filter dialog with custom footer when customFooter is provided', () => {
const CustomFooter = () => ;
- const options = { textLabels };
+ const options = { textLabels: getTextLabels() };
const filterList = [[], [], [], []];
const onFilterUpdate = spy();
@@ -208,7 +208,7 @@ describe('
', function() {
});
it('should trigger onFilterUpdate prop callback when calling method handleCheckboxChange', () => {
- const options = { filterType: 'checkbox', textLabels };
+ const options = { filterType: 'checkbox', textLabels: getTextLabels() };
const filterList = [[], [], [], []];
const onFilterUpdate = spy();
@@ -229,7 +229,7 @@ describe('
', function() {
});
it('should trigger onFilterUpdate prop callback when calling method handleDropdownChange', () => {
- const options = { filterType: 'select', textLabels };
+ const options = { filterType: 'select', textLabels: getTextLabels() };
const filterList = [[], [], [], []];
const onFilterUpdate = spy();
@@ -260,7 +260,7 @@ describe('
', function() {
});
it('should trigger onFilterUpdate prop callback when calling method handleMultiselectChange', () => {
- const options = { filterType: 'multiselect', textLabels };
+ const options = { filterType: 'multiselect', textLabels: getTextLabels() };
const filterList = [[], [], [], []];
const onFilterUpdate = spy();
@@ -292,7 +292,7 @@ describe('
', function() {
});
it('should trigger onFilterUpdate prop callback when calling method handleTextFieldChange', () => {
- const options = { filterType: 'textField', textLabels };
+ const options = { filterType: 'textField', textLabels: getTextLabels() };
const filterList = [[], [], [], []];
const onFilterUpdate = spy();
diff --git a/test/MUIDataTableHeadCell.test.js b/test/MUIDataTableHeadCell.test.js
index 2924ebcbb..340f7de96 100644
--- a/test/MUIDataTableHeadCell.test.js
+++ b/test/MUIDataTableHeadCell.test.js
@@ -2,7 +2,7 @@ import React from 'react';
import { spy, stub } from 'sinon';
import { mount, shallow } from 'enzyme';
import { assert, expect, should } from 'chai';
-import textLabels from '../src/textLabels';
+import getTextLabels from '../src/textLabels';
import TableHeadCell from '../src/components/TableHeadCell';
import TableCell from '@material-ui/core/TableCell';
import TableSortLabel from '@material-ui/core/TableSortLabel';
@@ -18,7 +18,7 @@ describe('
', function() {
});
it('should add custom props to header cell if "setCellHeaderProps" provided', () => {
- const options = { sort: true, textLabels };
+ const options = { sort: true, textLabels: getTextLabels() };
const toggleSort = () => {};
const setCellHeaderProps = { myProp: 'test', className: 'testClass' };
const selectRowUpdate = stub();
@@ -45,7 +45,7 @@ describe('
', function() {
});
it('should render a table head cell with sort label when options.sort = true provided', () => {
- const options = { sort: true, textLabels };
+ const options = { sort: true, textLabels: getTextLabels() };
const toggleSort = () => {};
const shallowWrapper = shallow(
@@ -59,7 +59,7 @@ describe('
', function() {
});
it('should render a table head cell without sort label when options.sort = false provided', () => {
- const options = { sort: false, textLabels };
+ const options = { sort: false, textLabels: getTextLabels() };
const toggleSort = () => {};
const shallowWrapper = shallow(
@@ -73,7 +73,7 @@ describe('
', function() {
});
it('should render a table help icon when hint provided', () => {
- const options = { sort: true, textLabels };
+ const options = { sort: true, textLabels: getTextLabels() };
const shallowWrapper = shallow(
@@ -86,7 +86,7 @@ describe('', function() {
});
it('should render a table head cell without custom tooltip when hint provided', () => {
- const options = { sort: true, textLabels };
+ const options = { sort: true, textLabels: getTextLabels() };
const shallowWrapper = shallow(
@@ -99,7 +99,7 @@ describe('', function() {
});
it('should trigger toggleSort prop callback when calling method handleSortClick', () => {
- const options = { sort: true, textLabels };
+ const options = { sort: true, textLabels: getTextLabels() };
const toggleSort = spy();
const shallowWrapper = shallow(
diff --git a/test/MUIDataTablePagination.test.js b/test/MUIDataTablePagination.test.js
index 71719c167..5e434a358 100644
--- a/test/MUIDataTablePagination.test.js
+++ b/test/MUIDataTablePagination.test.js
@@ -5,7 +5,7 @@ import { assert, expect, should } from 'chai';
import TableRow from '@material-ui/core/TableRow';
import TableFooter from '@material-ui/core/TableFooter';
import MuiTablePagination from '@material-ui/core/TablePagination';
-import textLabels from '../src/textLabels';
+import getTextLabels from '../src/textLabels';
import TablePagination from '../src/components/TablePagination';
describe('', function() {
@@ -14,7 +14,7 @@ describe('', function() {
before(() => {
options = {
rowsPerPageOptions: [5, 10, 15],
- textLabels,
+ textLabels: getTextLabels(),
};
});
diff --git a/test/MUIDataTableSearch.test.js b/test/MUIDataTableSearch.test.js
index e974beccb..e16e968c3 100644
--- a/test/MUIDataTableSearch.test.js
+++ b/test/MUIDataTableSearch.test.js
@@ -5,11 +5,11 @@ import { mount, shallow } from 'enzyme';
import { assert, expect, should } from 'chai';
import TextField from '@material-ui/core/TextField';
import TableSearch from '../src/components/TableSearch';
-import textLabels from '../src/textLabels';
+import getTextLabels from '../src/textLabels';
describe('', function() {
it('should render a search bar', () => {
- const options = { textLabels };
+ const options = { textLabels: getTextLabels() };
const onSearch = () => {};
const onHide = () => {};
@@ -20,7 +20,7 @@ describe('', function() {
});
it('should render a search bar with text initialized', () => {
- const options = { textLabels };
+ const options = { textLabels: getTextLabels() };
const onSearch = () => {};
const onHide = () => {};
@@ -33,7 +33,7 @@ describe('', function() {
});
it('should change search bar text when searchText changes', () => {
- const options = { textLabels };
+ const options = { textLabels: getTextLabels() };
const onSearch = () => {};
const onHide = () => {};
@@ -46,7 +46,7 @@ describe('', function() {
});
it('should render a search bar with placeholder when searchPlaceholder is set', () => {
- const options = { textLabels, searchPlaceholder: 'TestingPlaceholder' };
+ const options = { textLabels: getTextLabels(), searchPlaceholder: 'TestingPlaceholder' };
const onSearch = () => {};
const onHide = () => {};
@@ -57,7 +57,7 @@ describe('', function() {
});
it('should trigger handleTextChange prop callback when calling method handleTextChange', () => {
- const options = { onSearchChange: () => true, textLabels };
+ const options = { onSearchChange: () => true, textLabels: getTextLabels() };
const onSearch = spy();
const onHide = () => {};
@@ -70,7 +70,7 @@ describe('', function() {
});
it('should hide the search bar when hitting the ESCAPE key', () => {
- const options = { textLabels };
+ const options = { textLabels: getTextLabels() };
const onHide = spy();
const mountWrapper = mount(, { attachTo: document.body });
@@ -80,7 +80,7 @@ describe('', function() {
});
it('should hide not hide search bar when entering anything but the ESCAPE key', () => {
- const options = { textLabels };
+ const options = { textLabels: getTextLabels() };
const onHide = spy();
const mountWrapper = mount(, { attachTo: document.body });
diff --git a/test/MUIDataTableToolbar.test.js b/test/MUIDataTableToolbar.test.js
index 301dff104..6c0aca6ec 100644
--- a/test/MUIDataTableToolbar.test.js
+++ b/test/MUIDataTableToolbar.test.js
@@ -10,7 +10,7 @@ import React from 'react';
import { spy } from 'sinon';
import TableSearch from '../src/components/TableSearch';
import TableToolbar from '../src/components/TableToolbar';
-import textLabels from '../src/textLabels';
+import getTextLabels from '../src/textLabels';
describe('', function() {
let data;
@@ -25,7 +25,7 @@ describe('', function() {
search: true,
filter: true,
viewColumns: true,
- textLabels,
+ textLabels: getTextLabels(),
downloadOptions: {
separator: ',',
filename: 'tableDownload.csv',
@@ -193,6 +193,75 @@ describe('', function() {
assert.strictEqual(actualResult.length, 0);
});
+ it('should hide search when search icon is clicked while search is open without content', () => {
+ const searchTextUpdate = () => {};
+ const shallowWrapper = shallow(
+ {}}
+ searchTextUpdate={searchTextUpdate}
+ columns={columns}
+ data={data}
+ options={options}
+ setTableAction={setTableAction}
+ />,
+ ).dive();
+ const instance = shallowWrapper.instance();
+ instance.searchButton = {
+ focus: () => {},
+ };
+
+ // click search button to display search
+ shallowWrapper.find('[data-testid="Search-iconButton"]').simulate('click');
+ shallowWrapper.update();
+
+ assert.strictEqual(shallowWrapper.state('iconActive'), 'search');
+ let actualResult = shallowWrapper.find(TableSearch);
+ assert.strictEqual(actualResult.length, 1);
+
+ // now click search button again and test
+ shallowWrapper.find('[data-testid="Search-iconButton"]').simulate('click');
+ shallowWrapper.update();
+
+ assert.strictEqual(shallowWrapper.state('iconActive'), null);
+ actualResult = shallowWrapper.find(TableSearch);
+ assert.strictEqual(actualResult.length, 0);
+ });
+
+ it('should not hide search when search icon is clicked while search is open with content', () => {
+ const searchTextUpdate = () => {};
+ const shallowWrapper = shallow(
+ {}}
+ searchTextUpdate={searchTextUpdate}
+ columns={columns}
+ data={data}
+ options={options}
+ setTableAction={setTableAction}
+ />,
+ ).dive();
+ const instance = shallowWrapper.instance();
+ instance.searchButton = {
+ focus: () => {},
+ };
+
+ // click search button to display search
+ shallowWrapper.find('[data-testid="Search-iconButton"]').simulate('click');
+ shallowWrapper.update();
+
+ assert.strictEqual(shallowWrapper.state('iconActive'), 'search');
+ let actualResult = shallowWrapper.find(TableSearch);
+ assert.strictEqual(actualResult.length, 1);
+
+ // now set searchText and click search button again and test
+ shallowWrapper.setState({ searchText: 'fakeSearchText' });
+ shallowWrapper.find('[data-testid="Search-iconButton"]').simulate('click');
+ shallowWrapper.update();
+
+ assert.strictEqual(shallowWrapper.state('iconActive'), 'search');
+ actualResult = shallowWrapper.find(TableSearch);
+ assert.strictEqual(actualResult.length, 1);
+ });
+
it('should call onFilterDialogOpen when opening filters via toolbar', () => {
const onFilterDialogOpen = spy();
const newOptions = { ...options, onFilterDialogOpen };
@@ -236,6 +305,24 @@ describe('', function() {
assert.strictEqual(state.iconActive, 'filter');
});
+ it('should render search icon as active if option.searchOpen = true', () => {
+ const newOptions = { ...options, search: true, searchOpen: true };
+ const shallowWrapper = shallow(
+ ,
+ ).dive();
+ const actualResult = shallowWrapper.find('[data-testid="Search-iconButton"]');
+ assert.strictEqual(actualResult.prop('classes').root.indexOf('MUIDataTableToolbar-iconActive-'), 0);
+ });
+
+ it('should render search icon as active if option.searchText = some_text', () => {
+ const newOptions = { ...options, search: true, searchText: 'searchText' };
+ const shallowWrapper = shallow(
+ ,
+ ).dive();
+ const actualResult = shallowWrapper.find('[data-testid="Search-iconButton"]');
+ assert.strictEqual(actualResult.prop('classes').root.indexOf('MUIDataTableToolbar-iconActive-'), 0);
+ });
+
it('should download CSV when calling method handleCSVDownload', () => {
const shallowWrapper = shallow(
', function() {
before(() => {});
@@ -12,10 +11,14 @@ describe('', function() {
it('should render table toolbar select', () => {
const onRowsDelete = () => {};
const mountWrapper = mount(
- ,
+ ,
);
- const actualResult = mountWrapper.find(DeleteIcon);
+ const actualResult = mountWrapper.find('svg[data-icon="DeleteIcon"]');
assert.strictEqual(actualResult.length, 1);
});
@@ -27,7 +30,7 @@ describe('', function() {
const mountWrapper = mount(
', function() {
const mountWrapper = mount(
', function() {
const mountWrapper = mount(
', function() {
@@ -19,7 +19,7 @@ describe('', function() {
{ name: 'd', label: 'D' },
];
options = {
- textLabels,
+ textLabels: getTextLabels(),
};
});
diff --git a/test/mocha.opts b/test/mocha.opts
index 1074413f3..26898eb7b 100644
--- a/test/mocha.opts
+++ b/test/mocha.opts
@@ -1,4 +1,3 @@
--require ./test/setup-mocha-env.js
---compilers js:babel-core/register,jsx:babel-core/register
--extensions js,jsx
\ No newline at end of file
diff --git a/test/utils.test.js b/test/utils.test.js
index b74a74702..8af05284e 100644
--- a/test/utils.test.js
+++ b/test/utils.test.js
@@ -1,8 +1,17 @@
-import { getPageValue, buildCSV, createCSVDownload } from '../src/utils';
+import { getPageValue, buildCSV, createCSVDownload, escapeDangerousCSVCharacters } from '../src/utils';
import { spy } from 'sinon';
import { assert } from 'chai';
describe('utils.js', () => {
+ describe('escapeDangerousCSVCharacters', () => {
+ it('properly escapes the first character in a string if it can be used for injection', () => {
+ assert.strictEqual(escapeDangerousCSVCharacters('+SUM(1+1)'), "'+SUM(1+1)");
+ assert.strictEqual(escapeDangerousCSVCharacters('-SUM(1+1)'), "'-SUM(1+1)");
+ assert.strictEqual(escapeDangerousCSVCharacters('=SUM(1+1)'), "'=SUM(1+1)");
+ assert.strictEqual(escapeDangerousCSVCharacters('@SUM(1+1)'), "'@SUM(1+1)");
+ });
+ });
+
describe('getPageValue', () => {
it('returns the highest in bounds page value when page is out of bounds and count is greater than rowsPerPage', () => {
const count = 30;