diff --git a/Gruntfile.js b/Gruntfile.js index 728df8df..dadd7a21 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -45,6 +45,7 @@ module.exports = function(grunt) { 'tmp/reactable/unsafe.js': 'src/reactable/unsafe.jsx', 'tmp/reactable/th.js': 'src/reactable/th.jsx', 'tmp/reactable/paginator.js': 'src/reactable/paginator.jsx', + 'tmp/reactable/btnPaginator.js': 'src/reactable/btnPaginator.jsx', 'tmp/reactable/table.js': 'src/reactable/table.jsx', 'tmp/reactable.js': 'src/reactable.jsx', @@ -69,6 +70,7 @@ module.exports = function(grunt) { 'lib/reactable/unsafe.js': 'src/reactable/unsafe.jsx', 'lib/reactable/th.js': 'src/reactable/th.jsx', 'lib/reactable/paginator.js': 'src/reactable/paginator.jsx', + 'lib/reactable/btnPaginator.js': 'src/reactable/btnPaginator.jsx', 'lib/reactable/table.js': 'src/reactable/table.jsx', 'lib/reactable.js': 'src/reactable.jsx', @@ -94,6 +96,7 @@ module.exports = function(grunt) { 'tmp/reactable/th.js', 'tmp/reactable/thead.js', 'tmp/reactable/tfoot.js', + 'tmp/reactable/btnPaginator.js', 'tmp/reactable/paginator.js', 'tmp/reactable/table.js', 'tmp/reactable.js' @@ -131,4 +134,3 @@ module.exports = function(grunt) { grunt.registerTask('build', ['babel:common', 'buildBrowser']); grunt.registerTask('default', ['build', 'watch:build']); }; - diff --git a/build/reactable.js b/build/reactable.js index 5358fcc3..86f06c3b 100644 --- a/build/reactable.js +++ b/build/reactable.js @@ -26,7 +26,23 @@ window.ReactDOM["default"] = window.ReactDOM; itemsPerPage: true, childNode: true, data: true, - children: true + children: true, + topPagination: true, + itemsNumber: true, + numPages: true, + currentPage: true, + topPaginationElem: true, + filtering: true, + onFilter: true, + filterCleanBtn: true, + onClean: true, + onPageChange: true, + filterPlaceholder: true, + currentFilter: true, + sort: true, + sortableColumns: true, + onSort: true, + locale: true }; function filterPropsFrom(baseProps) { @@ -200,27 +216,27 @@ window.ReactDOM["default"] = window.ReactDOM; }); (function (global, factory) { - if (typeof define === 'function' && define.amd) { - define(['exports', 'react', 'react-dom'], factory); - } else if (typeof exports !== 'undefined') { - factory(exports, require('react'), require('react-dom')); + if (typeof define === "function" && define.amd) { + define(["exports", "react"], factory); + } else if (typeof exports !== "undefined") { + factory(exports, require("react")); } else { var mod = { exports: {} }; - factory(mod.exports, global.React, global.ReactDOM); + factory(mod.exports, global.React); global.filterer = mod.exports; } -})(this, function (exports, _react, _reactDom) { - 'use strict'; +})(this, function (exports, _react) { + "use strict"; - var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var FiltererInput = (function (_React$Component) { _inherits(FiltererInput, _React$Component); @@ -228,28 +244,43 @@ window.ReactDOM["default"] = window.ReactDOM; function FiltererInput() { _classCallCheck(this, FiltererInput); - _get(Object.getPrototypeOf(FiltererInput.prototype), 'constructor', this).apply(this, arguments); + _get(Object.getPrototypeOf(FiltererInput.prototype), "constructor", this).apply(this, arguments); } _createClass(FiltererInput, [{ - key: 'onChange', - value: function onChange() { - this.props.onFilter(_reactDom['default'].findDOMNode(this).value); - } - }, { - key: 'render', + key: "render", value: function render() { - return _react['default'].createElement('input', { type: 'text', - className: 'reactable-filter-input', - placeholder: this.props.placeholder, - value: this.props.value, - onKeyUp: this.onChange.bind(this), - onChange: this.onChange.bind(this) }); + var _props = this.props; + var onFilter = _props.onFilter; + var onClean = _props.onClean; + var filterCleanBtn = _props.filterCleanBtn; + var placeholder = _props.placeholder; + var value = _props.value; + + return _react["default"].createElement( + "div", + { className: "reactable-filter-input-container" }, + _react["default"].createElement("input", { + className: "reactable-filter-input", + placeholder: placeholder, + value: value, + onChange: function (ev) { + return onFilter(ev.target.value); + } + }), + filterCleanBtn && _react["default"].createElement( + "div", + { onClick: function () { + return onClean(); + }, className: "reactable-btn-clean" }, + "×" + ) + ); } }]); return FiltererInput; - })(_react['default'].Component); + })(_react["default"].Component); exports.FiltererInput = FiltererInput; ; @@ -260,32 +291,36 @@ window.ReactDOM["default"] = window.ReactDOM; function Filterer() { _classCallCheck(this, Filterer); - _get(Object.getPrototypeOf(Filterer.prototype), 'constructor', this).apply(this, arguments); + _get(Object.getPrototypeOf(Filterer.prototype), "constructor", this).apply(this, arguments); } _createClass(Filterer, [{ - key: 'render', + key: "render", value: function render() { if (typeof this.props.colSpan === 'undefined') { throw new TypeError('Must pass a colSpan argument to Filterer'); } - return _react['default'].createElement( - 'tr', - { className: 'reactable-filterer' }, - _react['default'].createElement( - 'td', + return _react["default"].createElement( + "tr", + { className: "reactable-filterer" }, + _react["default"].createElement( + "td", { colSpan: this.props.colSpan }, - _react['default'].createElement(FiltererInput, { onFilter: this.props.onFilter, + _react["default"].createElement(FiltererInput, { + filterCleanBtn: this.props.filterCleanBtn, + onClean: this.props.onClean, + onFilter: this.props.onFilter, value: this.props.value, - placeholder: this.props.placeholder }) + placeholder: this.props.placeholder + }) ) ); } }]); return Filterer; - })(_react['default'].Component); + })(_react["default"].Component); exports.Filterer = Filterer; ; @@ -443,12 +478,14 @@ window.ReactDOM["default"] = window.ReactDOM; onClick: this.handleClick.bind(this) }; + if (typeof this.props.style !== 'undefined') { + tdProps.style = this.props.style; + } + // Attach any properties on the column to this Td object to allow things like custom event handlers if (typeof this.props.column === 'object') { - for (var key in this.props.column) { - if (key !== 'key' && key !== 'name') { - tdProps[key] = this.props.column[key]; - } + for (var key in this.props.column.props) { + tdProps[key] = this.props.column[key]; } } @@ -622,17 +659,17 @@ window.ReactDOM["default"] = window.ReactDOM; (function (global, factory) { if (typeof define === 'function' && define.amd) { - define(['exports', 'react', './th', './filterer', './lib/filter_props_from'], factory); + define(['exports', 'react', './th', './filterer', './lib/filter_props_from', './btnPaginator'], factory); } else if (typeof exports !== 'undefined') { - factory(exports, require('react'), require('./th'), require('./filterer'), require('./lib/filter_props_from')); + factory(exports, require('react'), require('./th'), require('./filterer'), require('./lib/filter_props_from'), require('./btnPaginator')); } else { var mod = { exports: {} }; - factory(mod.exports, global.React, global.th, global.filterer, global.filter_props_from); + factory(mod.exports, global.React, global.th, global.filterer, global.filter_props_from, global.btnPaginator); global.thead = mod.exports; } -})(this, function (exports, _react, _th, _filterer, _libFilter_props_from) { +})(this, function (exports, _react, _th, _filterer, _libFilter_props_from, _btnPaginator) { 'use strict'; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; @@ -666,23 +703,51 @@ window.ReactDOM["default"] = window.ReactDOM; this.props.onSort(column.key); } } + }, { + key: 'scrollIntoView', + value: function scrollIntoView() { + if (this.domEl && this.domEl.offsetParent === null) { + this.domEl.scrollIntoView(); + } + } }, { key: 'render', value: function render() { + var _this = this; + + var _props = this.props; + var sort = _props.sort; + var sortableColumns = _props.sortableColumns; + var filtering = _props.filtering; + var columns = _props.columns; + var onFilter = _props.onFilter; + var onClean = _props.onClean; + var filterPlaceholder = _props.filterPlaceholder; + var currentFilter = _props.currentFilter; + var topPagination = _props.topPagination; + var filterCleanBtn = _props.filterCleanBtn; + var locale = _props.locale; + var itemsPerPage = _props.itemsPerPage; + var itemsNumber = _props.itemsNumber; + var numPages = _props.numPages; + var currentPage = _props.currentPage; + var onPageChange = _props.onPageChange; + var topPaginationElem = _props.topPaginationElem; + // Declare the list of Ths var Ths = []; - for (var index = 0; index < this.props.columns.length; index++) { - var column = this.props.columns[index]; + for (var index = 0; index < columns.length; index++) { + var column = columns[index]; var thClass = 'reactable-th-' + column.key.replace(/\s+/g, '-').toLowerCase(); var sortClass = ''; - if (this.props.sortableColumns[column.key]) { + if (sortableColumns[column.key]) { sortClass += 'reactable-header-sortable '; } - if (this.props.sort.column === column.key) { + if (sort.column === column.key) { sortClass += 'reactable-header-sort'; - if (this.props.sort.direction === 1) { + if (sort.direction === 1) { sortClass += '-asc'; } else { sortClass += '-desc'; @@ -712,16 +777,59 @@ window.ReactDOM["default"] = window.ReactDOM; // Manually transfer props var props = (0, _libFilter_props_from.filterPropsFrom)(this.props); - return _react['default'].createElement( 'thead', - props, - this.props.filtering === true ? _react['default'].createElement(_filterer.Filterer, { - colSpan: this.props.columns.length, - onFilter: this.props.onFilter, - placeholder: this.props.filterPlaceholder, - value: this.props.currentFilter - }) : null, + _extends({}, props, { ref: function ref(el) { + return _this.domEl = el; + } }), + !topPagination && filtering && _react['default'].createElement(_filterer.Filterer, { + colSpan: columns.length, + onFilter: onFilter, + placeholder: filterPlaceholder, + value: currentFilter + }), + topPagination ? _react['default'].createElement( + 'tr', + { className: 'reactable-btnPagination' }, + _react['default'].createElement( + 'td', + { colSpan: columns.length }, + _react['default'].createElement( + 'div', + { className: 'reactable-topDesign' }, + _react['default'].createElement( + 'div', + { className: 'reactable-leftElem' }, + topPaginationElem.left + ), + _react['default'].createElement( + 'div', + { className: filtering ? 'reactable-mainElem' : 'reactable-mainElem no-filter' }, + filtering && _react['default'].createElement(_filterer.FiltererInput, { + filterCleanBtn: filterCleanBtn, + onClean: onClean, + onFilter: onFilter, + placeholder: filterPlaceholder, + value: currentFilter + }), + _react['default'].createElement(_btnPaginator.BtnPaginator, { + locale: locale, + itemsPerPage: itemsPerPage, + itemsNumber: itemsNumber, + numPages: numPages, + currentPage: currentPage, + onPageChange: onPageChange, + key: 'paginator' + }) + ), + _react['default'].createElement( + 'div', + { className: 'reactable-rightElem' }, + topPaginationElem.right + ) + ) + ) + ) : null, _react['default'].createElement( 'tr', { className: 'reactable-column-header' }, @@ -739,20 +847,21 @@ window.ReactDOM["default"] = window.ReactDOM; if (typeof th.props !== 'undefined') { column.props = (0, _libFilter_props_from.filterPropsFrom)(th.props); - // use the content as the label & key - if (typeof th.props.children !== 'undefined') { + //set the label depending on the props + if (typeof th.props.label !== 'undefined') { + column.label = th.props.label; + } else if (typeof th.props.children !== 'undefined') { column.label = th.props.children; - column.key = column.label; + } else if (typeof th.props.column === 'string') { + column.label = th.props.column; } // the key in the column attribute supersedes the one defined previously if (typeof th.props.column === 'string') { column.key = th.props.column; - - // in case we don't have a label yet - if (typeof column.label === 'undefined') { - column.label = column.key; - } + } else { + // use the content as the key + column.key = th.props.children; } } @@ -819,19 +928,162 @@ window.ReactDOM["default"] = window.ReactDOM; exports.Tfoot = Tfoot; }); +(function (global, factory) { + if (typeof define === "function" && define.amd) { + define(["exports", "react"], factory); + } else if (typeof exports !== "undefined") { + factory(exports, require("react")); + } else { + var mod = { + exports: {} + }; + factory(mod.exports, global.React); + global.btnPaginator = mod.exports; + } +})(this, function (exports, _react) { + "use strict"; + + var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + + var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + + function pageHref(num) { + return "#page-" + (num + 1); + } + + var tr = { + of: { + nl: "van", + fr: "de", + de: "von", + en: "of" + } + }; + + var BtnPaginator = (function (_React$Component) { + _inherits(BtnPaginator, _React$Component); + + function BtnPaginator(props) { + _classCallCheck(this, BtnPaginator); + + _get(Object.getPrototypeOf(BtnPaginator.prototype), "constructor", this).call(this, props); + this.handlePrevious = this.handlePrevious.bind(this); + this.handleNext = this.handleNext.bind(this); + } + + _createClass(BtnPaginator, [{ + key: "handlePrevious", + value: function handlePrevious(e) { + e.preventDefault(); + this.props.onPageChange(this.props.currentPage - 1); + } + }, { + key: "handleNext", + value: function handleNext(e) { + e.preventDefault(); + this.props.onPageChange(this.props.currentPage + 1); + } + }, { + key: "renderPrevious", + value: function renderPrevious() { + var currentPage = this.props.currentPage; + + var canPage = currentPage > 0; + return _react["default"].createElement("button", { className: "reactable-previous-page " + (canPage ? "" : "disabled"), + href: pageHref(this.props.currentPage - 1), + onClick: canPage && this.handlePrevious }); + } + }, { + key: "renderNext", + value: function renderNext() { + var _props = this.props; + var currentPage = _props.currentPage; + var numPages = _props.numPages; + + var canPage = currentPage + 1 !== numPages; + return _react["default"].createElement("button", { className: "reactable-next-page " + (canPage ? "" : "disabled"), + href: pageHref(this.props.currentPage + 1), + onClick: canPage && this.handleNext }); + } + }, { + key: "calcRange", + value: function calcRange() { + var _props2 = this.props; + var currentPage = _props2.currentPage; + var itemsNumber = _props2.itemsNumber; + var itemsPerPage = _props2.itemsPerPage; + + var fromVal = currentPage === -1 ? 0 : currentPage * itemsPerPage + 1; + var toValTmp = (currentPage + 1) * itemsPerPage; + var toVal = toValTmp < itemsNumber ? toValTmp : itemsNumber; + return fromVal + " - " + toVal; + } + }, { + key: "renderCounter", + value: function renderCounter() { + var _props3 = this.props; + var itemsNumber = _props3.itemsNumber; + var locale = _props3.locale; + + return _react["default"].createElement( + "div", + { className: "counter" }, + _react["default"].createElement( + "span", + null, + this.calcRange(), + " ", + tr.of[locale], + " ", + itemsNumber + ) + ); + } + }, { + key: "render", + value: function render() { + + if (typeof this.props.currentPage === 'undefined') { + throw new TypeError('Must pass a currentPage argument to Paginator'); + } + return _react["default"].createElement( + "div", + { className: "reactable-pagesSwitcher" }, + this.renderCounter(), + _react["default"].createElement( + "div", + { className: "reactable-pageBtns" }, + this.renderPrevious(), + this.renderNext() + ) + ); + } + }]); + + return BtnPaginator; + })(_react["default"].Component); + + exports.BtnPaginator = BtnPaginator; + ; +}); + (function (global, factory) { if (typeof define === 'function' && define.amd) { - define(['exports', 'react'], factory); + define(['exports', 'react', './btnPaginator'], factory); } else if (typeof exports !== 'undefined') { - factory(exports, require('react')); + factory(exports, require('react'), require('./btnPaginator')); } else { var mod = { exports: {} }; - factory(mod.exports, global.React); + factory(mod.exports, global.React, global.btnPaginator); global.paginator = mod.exports; } -})(this, function (exports, _react) { +})(this, function (exports, _react, _btnPaginator) { 'use strict'; var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); @@ -876,33 +1128,46 @@ window.ReactDOM["default"] = window.ReactDOM; }, { key: 'renderPrevious', value: function renderPrevious() { + var previousBtn = { + nl: "Vorige", + fr: "Précédente", + en: "Previous" + }; + var locale = this.props.locale; + if (this.props.currentPage > 0) { return _react['default'].createElement( 'a', { className: 'reactable-previous-page', href: pageHref(this.props.currentPage - 1), onClick: this.handlePrevious.bind(this) }, - 'Previous' + previousBtn[locale] || previousBtn.en ); } } }, { key: 'renderNext', value: function renderNext() { + var nextBtn = { + nl: "Volgende", + fr: "Suivante", + en: "Next" + }; + var locale = this.props.locale; + if (this.props.currentPage < this.props.numPages - 1) { return _react['default'].createElement( 'a', { className: 'reactable-next-page', href: pageHref(this.props.currentPage + 1), onClick: this.handleNext.bind(this) }, - 'Next' + nextBtn[locale] || nextBtn.en ); } } }, { key: 'renderPageButton', value: function renderPageButton(className, pageNum) { - return _react['default'].createElement( 'a', { className: className, @@ -928,9 +1193,17 @@ window.ReactDOM["default"] = window.ReactDOM; } var pageButtons = []; - var pageButtonLimit = this.props.pageButtonLimit; - var currentPage = this.props.currentPage; - var numPages = this.props.numPages; + var _props = this.props; + var locale = _props.locale; + var bottomPaginationElem = _props.bottomPaginationElem; + var onPageChange = _props.onPageChange; + var itemsNumber = _props.itemsNumber; + var itemsPerPage = _props.itemsPerPage; + var bottomPagination = _props.bottomPagination; + var pageButtonLimit = _props.pageButtonLimit; + var currentPage = _props.currentPage; + var numPages = _props.numPages; + var lowerHalf = Math.round(pageButtonLimit / 2); var upperHalf = pageButtonLimit - lowerHalf; @@ -955,11 +1228,44 @@ window.ReactDOM["default"] = window.ReactDOM; if (numPages - currentPage > upperHalf) { pageButtons.splice(pageButtonLimit, pageButtons.length - pageButtonLimit); } - return _react['default'].createElement( 'tbody', { className: 'reactable-pagination' }, - _react['default'].createElement( + bottomPagination ? _react['default'].createElement( + 'tr', + { className: 'reactable-btnPagination' }, + _react['default'].createElement( + 'td', + { colSpan: this.props.colSpan }, + _react['default'].createElement( + 'div', + { className: 'reactable-bottomDesign' }, + _react['default'].createElement( + 'div', + { className: 'reactable-leftElem' }, + bottomPaginationElem.left + ), + _react['default'].createElement( + 'div', + { className: 'reactable-mainElem' }, + _react['default'].createElement(_btnPaginator.BtnPaginator, { + locale: locale, + itemsPerPage: itemsPerPage, + itemsNumber: itemsNumber, + numPages: numPages, + currentPage: currentPage, + onPageChange: onPageChange, + key: 'paginator' + }) + ), + _react['default'].createElement( + 'div', + { className: 'reactable-rightElem' }, + bottomPaginationElem.right + ) + ) + ) + ) : _react['default'].createElement( 'tr', null, _react['default'].createElement( @@ -1093,7 +1399,6 @@ window.ReactDOM["default"] = window.ReactDOM; } else if (typeof descendant.props.children !== 'undefined') { value = descendant.props.children; } else { - console.warn('exports.Td specified without ' + 'a `data` property or children, ' + 'ignoring'); return; } @@ -1216,7 +1521,7 @@ window.ReactDOM["default"] = window.ReactDOM; value: function componentWillMount() { this.initialize(this.props); this.sortByCurrentSort(); - this.filterBy(this.props.filterBy); + this.filterBy(this.props.filterBy != null ? this.props.filterBy : this.state.filter); } }, { key: 'componentWillReceiveProps', @@ -1224,7 +1529,51 @@ window.ReactDOM["default"] = window.ReactDOM; this.initialize(nextProps); this.updateCurrentSort(nextProps.sortBy); this.sortByCurrentSort(); - this.filterBy(nextProps.filterBy); + this.filterBy(nextProps.filterBy != null ? nextProps.filterBy : this.state.filter); + } + }, { + key: 'componentDidUpdate', + value: function componentDidUpdate() { + this.diffChildren(); + } + }, { + key: 'componentDidMount', + value: function componentDidMount() { + this.diffChildren(); + } + }, { + key: 'diffChildren', + value: function diffChildren() { + var onVisibleChange = this.props.onVisibleChange; + var lastChildren = this.lastChildren; + var lastIds = this.lastIds; + var currentChildren = this.currentChildren; + + if (!onVisibleChange || !currentChildren) { + return; + } + + if (lastChildren === currentChildren) { + return; + } + var currentIds = this.childrenToData(currentChildren, true); + + var same = undefined; + if (lastIds && lastIds.length === currentIds.length) { + same = true; + for (var i = 0; i < currentIds.length; i++) { + if (lastIds[i] !== currentIds[i]) { + same = false; + break; + } + } + } + + this.lastIds = currentIds; + this.lastChildren = currentChildren; + if (!same) { + onVisibleChange(currentIds); + } } }, { key: 'applyFilter', @@ -1313,6 +1662,42 @@ window.ReactDOM["default"] = window.ReactDOM; this.props.onSort(currentSort); } } + }, { + key: 'childrenToData', + value: function childrenToData(children, onlyIds) { + return children.map(function (row) { + var _row$props = row.props; + var i = _row$props.i; + var id = _row$props.id; + + var iOrId = i != null ? i : id; + var idOrKey = iOrId != null ? iOrId : row.key; + + if (onlyIds) { + return idOrKey; + } + + var data = { id: idOrKey }; + Object.keys(row.props.data).forEach(function (key) { + var col = row.props.data[key]; + data[key] = col.props.value != null ? col.props.value : col.value; + }); + + return data; + }); + } + }, { + key: 'visibleItems', + value: function visibleItems(onlyIds) { + return this.childrenToData(onlyIds); + } + }, { + key: 'scrollToTop', + value: function scrollToTop() { + if (this.headEl) { + this.headEl.scrollIntoView(); + } + } }, { key: 'render', value: function render() { @@ -1387,11 +1772,12 @@ window.ReactDOM["default"] = window.ReactDOM; // Determine if we render the filter box var filtering = false; - if (this.props.filterable && Array.isArray(this.props.filterable) && this.props.filterable.length > 0 && !this.props.hideFilterInput) { + if (Array.isArray(this.props.filterable) && this.props.filterable.length > 0 && !this.props.hideFilterInput) { filtering = true; } // Apply filters + var filterCleanBtn = this.props.filterCleanBtn && this.state.filter; var filteredChildren = children; if (this.state.filter !== '') { filteredChildren = this.applyFilter(this.state.filter, filteredChildren); @@ -1400,6 +1786,8 @@ window.ReactDOM["default"] = window.ReactDOM; // Determine pagination properties and which columns to display var itemsPerPage = 0; var pagination = false; + var topPagination = this.props.topPagination || false; + var bottomPagination = this.props.bottomPagination || false; var numPages = undefined; var currentPage = this.state.currentPage; var pageButtonLimit = this.props.pageButtonLimit || 10; @@ -1413,7 +1801,7 @@ window.ReactDOM["default"] = window.ReactDOM; currentPage = numPages - 1; } - pagination = true; + pagination = numPages > 1; currentChildren = filteredChildren.slice(currentPage * itemsPerPage, (currentPage + 1) * itemsPerPage); } @@ -1430,39 +1818,70 @@ window.ReactDOM["default"] = window.ReactDOM; ) ) : null; + this.currentChildren = currentChildren; return _react['default'].createElement( 'table', - props, - columns && columns.length > 0 ? _react['default'].createElement(_thead.Thead, { columns: columns, + null, + columns && columns.length > 0 ? _react['default'].createElement(_thead.Thead, { + ref: function (t) { + return _this.headEl = t; + }, + columns: columns, + topPagination: topPagination, + itemsNumber: filteredChildren.length, + itemsPerPage: itemsPerPage, + numPages: numPages, + currentPage: currentPage, + topPaginationElem: { + left: this.props.topPaginationElemL, + right: this.props.topPaginationElemR + }, filtering: filtering, onFilter: function (filter) { _this.setState({ filter: filter }); - if (_this.props.onFilter) { - _this.props.onFilter(filter); - } + }, + filterCleanBtn: filterCleanBtn, + onClean: function (filter) { + _this.setState({ filter: '' }); + }, + onPageChange: function (page) { + var onPageChange = _this.props.onPageChange; + + onPageChange && onPageChange(page); + _this.setState({ currentPage: page }); + _this.scrollToTop(); }, filterPlaceholder: this.props.filterPlaceholder, currentFilter: this.state.filter, sort: this.state.currentSort, sortableColumns: this._sortable, onSort: this.onSort.bind(this), - key: 'thead' }) : null, + key: 'thead', + locale: this.props.locale + }) : null, _react['default'].createElement( 'tbody', { className: 'reactable-data', key: 'tbody' }, currentChildren.length > 0 ? currentChildren : noDataText ), - pagination === true ? _react['default'].createElement(_paginator.Paginator, { colSpan: columns.length, + pagination ? _react['default'].createElement(_paginator.Paginator, { bottomPagination: bottomPagination, + itemsNumber: filteredChildren.length, + itemsPerPage: itemsPerPage, + locale: this.props.locale, + colSpan: columns.length, pageButtonLimit: pageButtonLimit, numPages: numPages, currentPage: currentPage, + bottomPaginationElem: { + left: this.props.bottomPaginationElemL, + right: this.props.bottomPaginationElemR + }, onPageChange: function (page) { _this.setState({ currentPage: page }); - if (_this.props.onPageChange) { - _this.props.onPageChange(page); - } + _this.scrollToTop(); }, - key: 'paginator' }) : null, + key: 'paginator' + }) : null, this.tfoot ); } @@ -1478,8 +1897,8 @@ window.ReactDOM["default"] = window.ReactDOM; defaultSort: false, defaultSortDescending: false, itemsPerPage: 0, - filterBy: '', - hideFilterInput: false + hideFilterInput: false, + locale: 'en' }; }); diff --git a/build/tests/reactable_test.js b/build/tests/reactable_test.js index 2412cb73..a327bdaa 100644 --- a/build/tests/reactable_test.js +++ b/build/tests/reactable_test.js @@ -1,2469 +1,27 @@ -(function (global, factory) { - if (typeof define === 'function' && define.amd) { - define(['exports'], factory); - } else if (typeof exports !== 'undefined') { - factory(exports); - } else { - var mod = { - exports: {} - }; - factory(mod.exports); - global.reactable_test = mod.exports; - } -})(this, function (exports) { - 'use strict'; - - var ReactTestUtils = React.addons.TestUtils; - var expect = chai.expect; - - var ReactableTestUtils = { - resetTestEnvironment: function resetTestEnvironment() { - ReactDOM.unmountComponentAtNode($('div#test-node')[0]); - $('div#test-node').remove(); - }, - - // Expect the row specified to have the specified class - expectRowClass: function expectRowClass(rowIndex, className) { - var row = $($('#table tbody.reactable-data tr')[rowIndex]); - expect(row).to.have['class'](className); - }, - - // Expect the columns of a the data row specified to have the values in the array as their text values - expectRowText: function expectRowText(rowIndex, textArray) { - var row = $($('#table tbody.reactable-data tr')[rowIndex]).find('td'); - - expect(row.length).to.equal(textArray.length); - - for (var i = 0; i < row.length; i++) { - expect($(row[i])).to.have.text(textArray[i]); - } - }, - - testNode: function testNode() { - var testNode = $('
').attr('id', 'test-node'); - $('body').append(testNode); - testNode.empty(); - return testNode[0]; - } - }; - - describe('Reactable', function () { - describe('directly passing a data array', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ Name: 'Griffin Smith', Age: '18' }, { Age: '23', Name: 'Lee Salminen' }, { Age: '28', Position: 'Developer' }, { Name: 'Leonor Hyatt', Position: null }] }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders the table', function () { - expect($('table#table.table')).to.exist; - }); - - it('renders the column headers in the table', function () { - var headers = []; - $('thead th').each(function () { - headers.push($(this).text()); - }); - - expect(headers).to.eql(['Name', 'Age', 'Position']); - }); - - it('renders the first row with the correct data', function () { - ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', '']); - }); - - it('renders the second row with the correct data', function () { - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', '']); - }); - - it('renders the third row with the correct data', function () { - ReactableTestUtils.expectRowText(2, ['', '28', 'Developer']); - }); - - it('handles null values', function () { - ReactableTestUtils.expectRowText(3, ['Leonor Hyatt', '', '']); - }); - }); - - describe('adding s to the ', function () { - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table' }, - React.createElement(Reactable.Tr, { data: { Name: 'Griffin Smith', Age: '18' } }), - React.createElement(Reactable.Tr, { data: { Age: '23', Name: 'Lee Salminen' } }), - React.createElement(Reactable.Tr, { data: { Age: '28', Position: 'Developer' } }) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders the table', function () { - expect($('table#table.table')).to.exist; - }); - - it('renders the column headers in the table', function () { - var headers = []; - $('thead th').each(function () { - headers.push($(this).text()); - }); - - expect(headers).to.eql(['Name', 'Age', 'Position']); - }); - - it('renders the first row with the correct data', function () { - ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', '']); - }); - - it('renders the second row with the correct data', function () { - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', '']); - }); - - it('renders the third row with the correct data', function () { - ReactableTestUtils.expectRowText(2, ['', '28', 'Developer']); - }); - }); - - describe('adding s', function () { - context('with only one s', function () { - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table' }, - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name' }, - React.createElement( - 'b', - null, - 'Griffin Smith' - ) - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - React.createElement( - 'em', - null, - '18' - ) - ) - ), - null, - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Position' }, - React.createElement( - 'b', - null, - 'Developer' - ) - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - React.createElement( - 'em', - null, - '28' - ) - ) - ) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders the table', function () { - expect($('table#table.table')).to.exist; - }); - - it('renders the column headers in the table', function () { - var headers = []; - $('thead th').each(function () { - headers.push($(this).text()); - }); - - expect(headers).to.eql(['Name', 'Age', 'Position']); - }); - - it('renders the first row with the correct data', function () { - ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', '']); - }); - - it('renders the second row with the correct data', function () { - ReactableTestUtils.expectRowText(1, ['', '28', 'Developer']); - }); - }); - }); - - describe('Adding a ', function () { - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table', sortable: ['Name'], filterable: ['Name', 'Age'] }, - React.createElement(Reactable.Tr, { className: 'rowClass1', data: { Name: 'Griffin Smith', Age: '18' } }), - React.createElement(Reactable.Tr, { className: 'rowClass2', data: { Age: '23', Name: 'Lee Salminen' } }), - React.createElement(Reactable.Tr, { className: 'rowClass3', data: { Age: '28', Position: 'Developer' } }), - React.createElement( - Reactable.Tfoot, - { id: 'tfoot' }, - React.createElement( - 'tr', - null, - React.createElement( - 'td', - { id: 'tfoot-stuff' }, - 'Test' - ) - ) - ) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders the table', function () { - expect($('#table')).to.exist; - }); - - it('renders the regular data rows', function () { - ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', '']); - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', '']); - ReactableTestUtils.expectRowText(2, ['', '28', 'Developer']); - }); - - it('renders the tfoot', function () { - expect($('#tfoot')).to.exist; - }); - - it('renders the children of the tfoot', function () { - expect($('#tfoot-stuff')).to.exist; - }); - - context('when sorting', function () { - before(function () { - ReactTestUtils.Simulate.click($('th')[0]); - }); - - it('leaves the tfoot alone', function () { - expect($('table :last-child')).to.match('tfoot'); - }); - }); - - context('when filtering', function () { - before(function () { - var $filter = $('.reactable-filter-input'); - - $filter.val('griffin'); - ReactTestUtils.Simulate.keyUp($filter[0]); - }); - - it('leaves the tfoot alone', function () { - expect($('table :last-child')).to.match('tfoot'); - }); - }); - }); - - describe('passing through HTML props', function () { - describe('adding s with className to the
s to the
', function () { - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table' }, - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name' }, - 'Griffin Smith' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name' }, - 'Lee Salminen' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name' }, - 'Ian Zhang' - ) - ) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders the table', function () { - expect($('table#table.table')).to.exist; - }); - - it('renders the column headers in the table', function () { - var headers = []; - $('thead th').each(function () { - headers.push($(this).text()); - }); - - expect(headers).to.eql(['Name']); - }); - - it('renders the first row with the correct data', function () { - ReactableTestUtils.expectRowText(0, ['Griffin Smith']); - }); - - it('renders the second row with the correct data', function () { - ReactableTestUtils.expectRowText(1, ['Lee Salminen']); - }); - - it('renders the third row with the correct data', function () { - ReactableTestUtils.expectRowText(2, ['Ian Zhang']); - }); - }); - - context('with multiple s', function () { - context('with plain text', function () { - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table' }, - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name' }, - 'Griffin Smith' - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - '18' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name' }, - 'Lee Salminen' - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - '23' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Position' }, - 'Developer' - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - '28' - ) - ) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders the table', function () { - expect($('table#table.table')).to.exist; - }); - - it('renders the column headers in the table', function () { - var headers = []; - $('thead th').each(function () { - headers.push($(this).text()); - }); - - expect(headers).to.eql(['Name', 'Age', 'Position']); - }); - - it('renders the first row with the correct data', function () { - ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', '']); - }); - - it('renders the second row with the correct data', function () { - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', '']); - }); - - it('renders the third row with the correct data', function () { - ReactableTestUtils.expectRowText(2, ['', '28', 'Developer']); - }); - }); - }); - - context('with React.DOM nodes inside', function () { - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table' }, - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name' }, - React.createElement( - 'b', - null, - 'Griffin Smith' - ) - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - React.createElement( - 'em', - null, - '18' - ) - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name' }, - React.createElement( - 'b', - null, - 'Lee Salminen' - ) - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - React.createElement( - 'em', - null, - '23' - ) - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Position' }, - React.createElement( - 'b', - null, - 'Developer' - ) - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - React.createElement( - 'em', - null, - '28' - ) - ) - ) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders the table', function () { - expect($('table#table.table')).to.exist; - }); - - it('renders the column headers in the table', function () { - var headers = []; - $('thead th').each(function () { - headers.push($(this).text()); - }); - - expect(headers).to.eql(['Name', 'Age', 'Position']); - }); - - it('renders the first row with the correct data', function () { - ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', '']); - }); - - it('renders the second row with the correct data', function () { - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', '']); - }); - - it('renders the third row with the correct data', function () { - ReactableTestUtils.expectRowText(2, ['', '28', 'Developer']); - }); - }); - - context('with null s', function () { - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table' }, - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name' }, - React.createElement( - 'b', - null, - 'Griffin Smith' - ) - ), - null - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name' }, - React.createElement( - 'b', - null, - 'Lee Salminen' - ) - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - React.createElement( - 'em', - null, - '23' - ) - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Position' }, - React.createElement( - 'b', - null, - 'Developer' - ) - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - React.createElement( - 'em', - null, - '28' - ) - ) - ) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders the table', function () { - expect($('table#table.table')).to.exist; - }); - - it('renders the column headers in the table', function () { - var headers = []; - $('thead th').each(function () { - headers.push($(this).text()); - }); - - expect(headers).to.eql(['Name', 'Age', 'Position']); - }); - - it('renders the first row with the correct data', function () { - ReactableTestUtils.expectRowText(0, ['Griffin Smith', '', '']); - }); - - it('renders the second row with the correct data', function () { - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', '']); - }); - - it('renders the third row with the correct data', function () { - ReactableTestUtils.expectRowText(2, ['', '28', 'Developer']); - }); - }); - - context('with null
', function () { - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table' }, - React.createElement(Reactable.Tr, { className: 'rowClass1', data: { Name: 'Griffin Smith', Age: '18' } }), - React.createElement(Reactable.Tr, { className: 'rowClass2', data: { Age: '23', Name: 'Lee Salminen' } }), - React.createElement(Reactable.Tr, { className: 'rowClass3', data: { Age: '28', Position: 'Developer' } }) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders the table', function () { - expect($('table#table.table')).to.exist; - }); - - it('renders the column headers in the table', function () { - var headers = []; - $('thead th').each(function () { - headers.push($(this).text()); - }); - - expect(headers).to.eql(['Name', 'Age', 'Position']); - }); - - it('renders the first row with the correct class name', function () { - ReactableTestUtils.expectRowClass(0, 'rowClass1'); - }); - - it('renders the second row with the correct class name', function () { - ReactableTestUtils.expectRowClass(1, 'rowClass2'); - }); - - it('renders the third row with the correct class name', function () { - ReactableTestUtils.expectRowClass(2, 'rowClass3'); - }); - }); - - describe('adding
s with classNames to the ', function () { - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table' }, - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name', className: 'name-1' }, - 'Griffin Smith' - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - '18' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name', className: 'name-2' }, - 'Lee Salminen' - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - '23' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Position', className: 'position' }, - 'Developer' - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - '28' - ) - ) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders the first column with the correct class name', function () { - expect($('td.name-1')).to.have.text('Griffin Smith'); - }); - - it('renders the second column with the correct class name', function () { - expect($('td.name-2')).to.have.text('Lee Salminen'); - }); - - it('renders the third column with the correct class name', function () { - expect($('td.position')).to.have.text('Developer'); - }); - }); - }); - - describe('specifying an array of columns', function () { - describe('as strings', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ Name: 'Griffin Smith', Age: '18', HideThis: 'one' }, { Age: '23', Name: 'Lee Salminen', HideThis: 'two' }, { Age: '28', Position: 'Developer' }], columns: ['Name', 'Age'] }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('omits columns not in the list', function () { - var columns = $('tr.reactable-column-header th'); - expect(columns.length).to.equal(2); - expect($(columns[0])).to.have.text('Name'); - expect($(columns[1])).to.have.text('Age'); - }); - - it('adds class name for each column base on its label', function () { - var columns = $('tr.reactable-column-header th'); - expect($(columns[0])).to.have['class']('reactable-th-name'); - expect($(columns[1])).to.have['class']('reactable-th-age'); - }); - }); - - describe('as objects', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ name: 'Griffin Smith', age: '18', HideThis: 'one' }, { age: '23', name: 'Lee Salminen', HideThis: 'two' }, { age: '28', Position: 'Developer' }], columns: [{ key: 'name', label: 'Name' }, { key: 'age', label: 'Age' }] }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('omits columns not in the list', function () { - var columns = $('tr.reactable-column-header th'); - expect(columns.length).to.equal(2); - }); - - it('allows changing the labels of the columns', function () { - var columns = $('tr.reactable-column-header th'); - expect($(columns[0])).to.have.text('Name'); - expect($(columns[1])).to.have.text('Age'); - }); - - it('adds class name for each column base on its key', function () { - var columns = $('tr.reactable-column-header th'); - expect($(columns[0])).to.have['class']('reactable-th-name'); - expect($(columns[1])).to.have['class']('reactable-th-age'); - }); - }); - }); - - describe('specifying columns using a ', function () { - describe('and an element for the column title', function () { - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { id: 'table', data: [{ Name: Reactable.unsafe('Griffin Smith'), Age: '18' }, { Age: '28', Position: Reactable.unsafe('Developer') }, { Age: '23', Name: Reactable.unsafe('Lee Salminen') }] }, - React.createElement( - Reactable.Thead, - null, - React.createElement( - Reactable.Th, - { column: 'Name', id: 'my-name' }, - React.createElement( - 'strong', - null, - 'name' - ) - ) - ) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders only the columns in the Thead', function () { - expect($('#table tbody tr:first td')).to.exist; - expect($('#table thead tr:first th')).to.exist; - }); - - it('renders the contents of the Th', function () { - expect($('#table>thead>tr>th>strong')).to.exist; - }); - - it('passes through the properties of the Th', function () { - expect($('#table>thead>tr>th')).to.have.id('my-name'); - }); - }); - - describe('and a string for the column title', function () { - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { id: 'table', data: [{ Name: Reactable.unsafe('Griffin Smith'), Age: '18' }, { Age: '28', Position: Reactable.unsafe('Developer') }, { Age: '23', Name: Reactable.unsafe('Lee Salminen') }] }, - React.createElement( - Reactable.Thead, - null, - React.createElement( - Reactable.Th, - { column: 'Name', id: 'my-name' }, - 'name' - ) - ) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders only the columns in the Thead', function () { - expect($('#table tbody tr:first td')).to.exist; - expect($('#table thead tr:first th')).to.exist; - }); - - it('renders the contents of the Th', function () { - expect($('#table>thead>tr>th')).to.exist; - }); - - it('passes through the properties of the Th', function () { - expect($('#table>thead>tr>th')).to.have.id('my-name'); - }); - }); - }); - - describe('unsafe() strings', function () { - context('in the
directly', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ Name: Reactable.unsafe('Griffin Smith'), Age: '18' }, { Age: '28', Position: Reactable.unsafe('Developer') }, { Age: '23', Name: Reactable.unsafe('Lee Salminen') }], sortable: ['Name'] }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders the HTML in the table cells', function () { - var griffins_name = $('span#griffins-name'); - expect(griffins_name.length).to.equal(1); - expect(griffins_name).to.have.text('Griffin Smith'); - - var lees_name = $('span#lees-name'); - expect(lees_name.length).to.equal(1); - expect(lees_name).to.have.text('Lee Salminen'); - - var who_knows_job = $('span#who-knows-job'); - expect(who_knows_job.length).to.equal(1); - expect(who_knows_job).to.have.text('Developer'); - }); - - it('still allows sorting', function () { - var nameHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.click(nameHeader); - - ReactableTestUtils.expectRowText(0, ['28', 'Developer', '']); - ReactableTestUtils.expectRowText(1, ['18', '', 'Griffin Smith']); - ReactableTestUtils.expectRowText(2, ['23', '', 'Lee Salminen']); - }); - }); - - context('in column labels', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ Name: 'Griffin Smith', Age: '18' }, { Age: '23', Name: 'Lee Salminen' }, { Age: '28', Position: 'Developer' }], columns: [{ key: 'Name', label: Reactable.unsafe('Name') }, { key: 'Age', label: Reactable.unsafe('Age') }, { key: 'Position', label: Reactable.unsafe('Position') }] }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders the HTML in the column headers', function () { - var headers = []; - $('thead th').each(function () { - headers.push($(this).html()); - }); - - expect(headers).to.eql(['Name', 'Age', 'Position']); - }); - }); - - context('in the s', function () { - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table' }, - React.createElement(Reactable.Tr, { data: { Name: Reactable.unsafe('Griffin Smith'), Age: '18' } }), - React.createElement(Reactable.Tr, { data: { Age: '23', Name: Reactable.unsafe('Lee Salminen') } }), - React.createElement(Reactable.Tr, { data: { Age: '28', Position: Reactable.unsafe('Developer') } }) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders the HTML in the table cells', function () { - var griffins_name = $('span#griffins-name'); - expect(griffins_name.length).to.equal(1); - expect(griffins_name).to.have.text('Griffin Smith'); - - var lees_name = $('span#lees-name'); - expect(lees_name.length).to.equal(1); - expect(lees_name).to.have.text('Lee Salminen'); - - var who_knows_job = $('span#who-knows-job'); - expect(who_knows_job.length).to.equal(1); - expect(who_knows_job).to.have.text('Developer'); - }); - }); - - context('in the s', function () { - before(function () { - this.component = ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', columns: ['State', 'Description', 'Tag'], noDataText: 'No matching records found.' }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('shows the "no data" message', function () { - var $text = $('.reactable-no-data').text(); - expect($text).to.eq('No matching records found.'); - }); - }); - - context('when filtered without any matches', function () { - before(function () { - this.component = ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table', - filterable: ['State', 'Tag'], - filterPlaceholder: 'Filter Results', - filterBy: 'xxxxx', - noDataText: 'No matching records found.', - columns: ['State', 'Description', 'Tag'] }, - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'State' }, - 'New York' - ), - React.createElement( - Reactable.Td, - { column: 'Description' }, - 'this is some text' - ), - React.createElement( - Reactable.Td, - { column: 'Tag' }, - 'new' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'State' }, - 'New Mexico' - ), - React.createElement( - Reactable.Td, - { column: 'Description' }, - 'lorem ipsum' - ), - React.createElement( - Reactable.Td, - { column: 'Tag' }, - 'old' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'State' }, - 'Colorado' - ), - React.createElement( - Reactable.Td, - { column: 'Description' }, - 'new description that shouldnt match filter' - ), - React.createElement( - Reactable.Td, - { column: 'Tag' }, - 'old' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'State' }, - 'Alaska' - ), - React.createElement( - Reactable.Td, - { column: 'Description' }, - 'bacon' - ), - React.createElement( - Reactable.Td, - { column: 'Tag' }, - 'renewed' - ) - ) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('shows the "no data" message', function () { - var text = $('.reactable-no-data').text(); - expect(text).to.eq('No matching records found.'); - }); - }); - - context('when initialized with an empty array for `data` prop', function () { - before(function () { - this.component = ReactDOM.render(React.createElement(Reactable.Table, { data: [], className: 'table', id: 'table', columns: ['State', 'Description', 'Tag'], noDataText: 'No matching records found.' }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('shows the "no data" message', function () { - var $text = $('.reactable-no-data').text(); - expect($text).to.eq('No matching records found.'); - }); - }); - }); - }); -}); +(function(global,factory){if(typeof define === 'function' && define.amd){define(['exports'],factory);}else if(typeof exports !== 'undefined'){factory(exports);}else {var mod={exports:{}};factory(mod.exports);global.reactable_test = mod.exports;}})(this,function(exports){'use strict';var _extends=Object.assign || function(target){for(var i=1;i < arguments.length;i++) {var source=arguments[i];for(var key in source) {if(Object.prototype.hasOwnProperty.call(source,key)){target[key] = source[key];}}}return target;};var _createClass=(function(){function defineProperties(target,props){for(var i=0;i < props.length;i++) {var descriptor=props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if('value' in descriptor)descriptor.writable = true;Object.defineProperty(target,descriptor.key,descriptor);}}return function(Constructor,protoProps,staticProps){if(protoProps)defineProperties(Constructor.prototype,protoProps);if(staticProps)defineProperties(Constructor,staticProps);return Constructor;};})();var _get=function get(_x2,_x3,_x4){var _again=true;_function: while(_again) {var object=_x2,property=_x3,receiver=_x4;_again = false;if(object === null)object = Function.prototype;var desc=Object.getOwnPropertyDescriptor(object,property);if(desc === undefined){var parent=Object.getPrototypeOf(object);if(parent === null){return undefined;}else {_x2 = parent;_x3 = property;_x4 = receiver;_again = true;desc = parent = undefined;continue _function;}}else if('value' in desc){return desc.value;}else {var getter=desc.get;if(getter === undefined){return undefined;}return getter.call(receiver);}}};function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError('Cannot call a class as a function');}}function _inherits(subClass,superClass){if(typeof superClass !== 'function' && superClass !== null){throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass);}subClass.prototype = Object.create(superClass && superClass.prototype,{constructor:{value:subClass,enumerable:false,writable:true,configurable:true}});if(superClass)Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__ = superClass;}var ReactTestUtils=React.addons.TestUtils;var expect=chai.expect;var ReactableTestUtils={resetTestEnvironment:function resetTestEnvironment(){ReactDOM.unmountComponentAtNode($('div#test-node')[0]);$('div#test-node').remove();}, // Expect the row specified to have the specified class +expectRowClass:function expectRowClass(rowIndex,className){var row=$($('#table tbody.reactable-data tr')[rowIndex]);expect(row).to.have['class'](className);}, // Expect the columns of a the data row specified to have the values in the array as their text values +expectRowText:function expectRowText(rowIndex,textArray){var row=$($('#table tbody.reactable-data tr')[rowIndex]).find('td');expect(row.length).to.equal(textArray.length);for(var i=0;i < row.length;i++) {expect($(row[i])).to.have.text(textArray[i]);}},testNode:function testNode(){var testNode=$('
').attr('id','test-node');$('body').append(testNode);testNode.empty();return testNode[0];}};describe('Reactable',function(){describe('directly passing a data array',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{Name:'Griffin Smith',Age:'18'},{Age:'23',Name:'Lee Salminen'},{Age:'28',Position:'Developer'},{Name:'Leonor Hyatt',Position:null}]}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the table',function(){expect($('table#table.table')).to.exist;});it('renders the column headers in the table',function(){var headers=[];$('thead th').each(function(){headers.push($(this).text());});expect(headers).to.eql(['Name','Age','Position']);});it('renders the first row with the correct data',function(){ReactableTestUtils.expectRowText(0,['Griffin Smith','18','']);});it('renders the second row with the correct data',function(){ReactableTestUtils.expectRowText(1,['Lee Salminen','23','']);});it('renders the third row with the correct data',function(){ReactableTestUtils.expectRowText(2,['','28','Developer']);});it('handles null values',function(){ReactableTestUtils.expectRowText(3,['Leonor Hyatt','','']);});});describe('adding
s to the
s', function () { - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table' }, - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name' }, - Reactable.unsafe('Griffin Smith') - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - '18' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name' }, - Reactable.unsafe('Lee Salminen') - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - '23' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Position' }, - Reactable.unsafe('Developer') - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - '28' - ) - ) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders the HTML in the table cells', function () { - var griffins_name = $('span#griffins-name'); - expect(griffins_name.length).to.equal(1); - expect(griffins_name).to.have.text('Griffin Smith'); - - var lees_name = $('span#lees-name'); - expect(lees_name.length).to.equal(1); - expect(lees_name).to.have.text('Lee Salminen'); - - var who_knows_job = $('span#who-knows-job'); - expect(who_knows_job.length).to.equal(1); - expect(who_knows_job).to.have.text('Developer'); - }); - }); - }); - - describe('pagination', function () { - describe('specifying pageButtonLimit', function () { - - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Test Person' }, { 'Name': 'Ian Zhang', 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18', 'Position': 'Software Developer' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Test Person' }, { 'Name': 'Ian Zhang', 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18', 'Position': 'Software Developer' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Test Person' }, { 'Name': 'Ian Zhang', 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18', 'Position': 'Software Developer' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }], itemsPerPage: 2, pageButtonLimit: 8 }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('shows no more page buttons than the pageButtonLimit', function () { - var pageButtons = $('#table tbody.reactable-pagination a.reactable-page-button'); - expect(pageButtons.length).to.equal(8); - }); - }); - describe('specifying itemsPerPage', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Test Person' }, { 'Name': 'Ian Zhang', 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18', 'Position': 'Software Developer' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }], itemsPerPage: 4 }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('provides buttons for each page', function () { - var pageButtons = $('#table tbody.reactable-pagination a.reactable-page-button'); - expect(pageButtons.length).to.equal(3); - expect($(pageButtons[0])).to.have.text('1'); - expect($(pageButtons[1])).to.have.text('2'); - expect($(pageButtons[2])).to.have.text('3'); - }); - - it('displays only the first n rows', function () { - expect($('#table tbody.reactable-data tr').length).to.equal(4); - }); - - it('specifies a class on the currently active page', function () { - var activePage = $('#table tbody.reactable-pagination a.reactable-page-button.reactable-current-page'); - expect(activePage.length).to.equal(1); - expect(activePage).to.have.text('1'); - }); - - it('does not show previous button', function () { - var previousButton = $('#table tbody.reactable-pagination a.reactable-previous-page'); - expect(previousButton.length).to.equal(0); - }); - - it('shows next button', function () { - var nextButton = $('#table tbody.reactable-pagination a.reactable-next-page'); - expect(nextButton.length).to.equal(1); - }); - - describe('clicking page buttons', function () { - beforeEach(function () { - var page2 = $('#table tbody.reactable-pagination a.reactable-page-button')[1]; - ReactTestUtils.Simulate.click(page2); - }); - - it('loads the next n rows', function () { - var rows = $('#table tbody.reactable-data tr'); - expect($($(rows[0]).find('td')[0])).to.have.text('Test Person'); - expect($($(rows[1]).find('td')[0])).to.have.text('Ian Zhang'); - expect($($(rows[2]).find('td')[0])).to.have.text('Griffin Smith'); - expect($($(rows[3]).find('td')[0])).to.have.text('Lee Salminen'); - }); - - it('puts an active class on the new active page', function () { - var activePage = $('#table tbody.reactable-pagination a.reactable-page-button.reactable-current-page'); - expect(activePage.length).to.equal(1); - expect(activePage).to.have.text('2'); - }); - - it('can go back to the original page', function () { - var page1 = $('#table tbody.reactable-pagination a.reactable-page-button')[0]; - ReactTestUtils.Simulate.click(page1); - - var rows = $('#table tbody.reactable-data tr'); - expect($($(rows[0]).find('td')[0])).to.have.text('Griffin Smith'); - expect($($(rows[1]).find('td')[0])).to.have.text('Lee Salminen'); - expect($($(rows[2]).find('td')[0])).to.have.text(''); - expect($($(rows[3]).find('td')[0])).to.have.text('Griffin Smith'); - }); - - it('shows previous button', function () { - var previousButton = $('#table tbody.reactable-pagination a.reactable-previous-page'); - expect(previousButton.length).to.equal(1); - }); - }); - }); - - describe('specifying more itemsPerPage than items', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Test Person' }, { 'Name': 'Ian Zhang', 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18', 'Position': 'Software Developer' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }], itemsPerPage: 20 }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders all rows', function () { - expect($('#table tbody.reactable-data tr').length).to.equal(9); - }); - - it('provides buttons for 1 page', function () { - var pageButtons = $('#table tbody.reactable-pagination a.reactable-page-button'); - expect(pageButtons.length).to.equal(1); - expect($(pageButtons[0])).to.have.text('1'); - }); - - it('does not show previous and next buttons', function () { - var previousButton = $('#table tbody.reactable-pagination a.reactable-previous-page'); - var nextButton = $('#table tbody.reactable-pagination a.reactable-next-page'); - expect(previousButton.length + nextButton.length).to.equal(0); - }); - }); - - describe('not specifying itemsPerPage', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Test Person' }, { 'Name': 'Ian Zhang', 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18', 'Position': 'Software Developer' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }] }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders all rows', function () { - expect($('#table tbody.reactable-data tr').length).to.equal(9); - }); - }); - - describe('specifying 0 itemsPerPage', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Test Person' }, { 'Name': 'Ian Zhang', 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18', 'Position': 'Software Developer' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }], itemsPerPage: 0 }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders all rows', function () { - expect($('#table tbody.reactable-data tr').length).to.equal(9); - }); - }); - - describe('onPageChange hook', function () { - var currentPage = undefined; - var callback = function callback(page) { - currentPage = page; - }; - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18' }, { 'Age': '23', 'Name': 'Test Person' }, { 'Name': 'Ian Zhang', 'Age': '28', 'Position': 'Developer' }, { 'Name': 'Griffin Smith', 'Age': '18', 'Position': 'Software Developer' }, { 'Age': '23', 'Name': 'Lee Salminen' }, { 'Age': '28', 'Position': 'Developer' }], itemsPerPage: 4, onPageChange: callback }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('emits the number of the currently selected page (zero based) when onPageChange event is triggered', function () { - var page1 = $('#table tbody.reactable-pagination a.reactable-page-button')[0]; - var page2 = $('#table tbody.reactable-pagination a.reactable-page-button')[1]; - var page3 = $('#table tbody.reactable-pagination a.reactable-page-button')[2]; - ReactTestUtils.Simulate.click(page2); - expect(currentPage).to.equal(1); - ReactTestUtils.Simulate.click(page1); - expect(currentPage).to.equal(0); - ReactTestUtils.Simulate.click(page3); - expect(currentPage).to.equal(2); - }); - }); - }); - - describe('sorting', function () { - describe('no default sort', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ Name: 'Lee Salminen', Age: '23', Position: 'Programmer' }, { Name: 'Griffin Smith', Age: '18', Position: 'Engineer' }, { Name: 'Ian Zhang', Age: '28', Position: 'Developer' }], - sortable: [{ - column: 'Name', - sortFunction: function sortFunction(a, b) { - // Sort by last name - var nameA = a.split(' '); - var nameB = b.split(' '); - - return nameA[1].localeCompare(nameB[1]); - } - }, 'Age', 'Position'] }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders all rows with no sort', function () { - ReactableTestUtils.expectRowText(0, ['Lee Salminen', '23', 'Programmer']); - ReactableTestUtils.expectRowText(1, ['Griffin Smith', '18', 'Engineer']); - ReactableTestUtils.expectRowText(2, ['Ian Zhang', '28', 'Developer']); - }); - - it('adds reactable-header-sortable to all headers', function () { - var header = $('#table thead tr.reactable-column-header th')[0]; - expect($(header)).to.have['class']('reactable-header-sortable'); - - header = $('#table thead tr.reactable-column-header th')[1]; - expect($(header)).to.have['class']('reactable-header-sortable'); - - header = $('#table thead tr.reactable-column-header th')[2]; - expect($(header)).to.have['class']('reactable-header-sortable'); - }); - - it('sorts by text in ascending order', function () { - var positionHeader = $('#table thead tr.reactable-column-header th')[2]; - ReactTestUtils.Simulate.click(positionHeader); - - ReactableTestUtils.expectRowText(0, ['Ian Zhang', '28', 'Developer']); - ReactableTestUtils.expectRowText(1, ['Griffin Smith', '18', 'Engineer']); - ReactableTestUtils.expectRowText(2, ['Lee Salminen', '23', 'Programmer']); - - // Make sure the headers have the right classes - expect($(positionHeader)).to.have['class']('reactable-header-sort-asc'); - }); - - it('sorts by text in descending order', function () { - var positionHeader = $('#table thead tr.reactable-column-header th')[2]; - ReactTestUtils.Simulate.click(positionHeader); - - ReactableTestUtils.expectRowText(0, ['Lee Salminen', '23', 'Programmer']); - ReactableTestUtils.expectRowText(1, ['Griffin Smith', '18', 'Engineer']); - ReactableTestUtils.expectRowText(2, ['Ian Zhang', '28', 'Developer']); - - // Make sure the headers have the right classes - expect($(positionHeader)).to.have['class']('reactable-header-sort-desc'); - }); - - it('sorts by last name in ascending order', function () { - var nameHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.click(nameHeader); - - ReactableTestUtils.expectRowText(0, ['Lee Salminen', '23', 'Programmer']); - ReactableTestUtils.expectRowText(1, ['Griffin Smith', '18', 'Engineer']); - ReactableTestUtils.expectRowText(2, ['Ian Zhang', '28', 'Developer']); - - // Make sure the headers have the right classes - expect($(nameHeader)).to.have['class']('reactable-header-sort-asc'); - }); - - it('sorts by last name in descending order', function () { - var nameHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.click(nameHeader); - - ReactableTestUtils.expectRowText(0, ['Ian Zhang', '28', 'Developer']); - ReactableTestUtils.expectRowText(1, ['Griffin Smith', '18', 'Engineer']); - ReactableTestUtils.expectRowText(2, ['Lee Salminen', '23', 'Programmer']); - - // Make sure the headers have the right classes - expect($(nameHeader)).to.have['class']('reactable-header-sort-desc'); - }); - - it('sorts by last name in ascending order by enter keydown', function () { - var nameHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.keyDown(nameHeader, { keyCode: 13 }); - - ReactableTestUtils.expectRowText(0, ['Lee Salminen', '23', 'Programmer']); - ReactableTestUtils.expectRowText(1, ['Griffin Smith', '18', 'Engineer']); - ReactableTestUtils.expectRowText(2, ['Ian Zhang', '28', 'Developer']); - - // Make sure the headers have the right classes - expect($(nameHeader)).to.have['class']('reactable-header-sort-asc'); - }); - - it('does not sort on non-enter keydown', function () { - var nameHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.keyDown(nameHeader, { keyCode: 10 }); - - ReactableTestUtils.expectRowText(0, ['Lee Salminen', '23', 'Programmer']); - ReactableTestUtils.expectRowText(1, ['Griffin Smith', '18', 'Engineer']); - ReactableTestUtils.expectRowText(2, ['Ian Zhang', '28', 'Developer']); - - // Make sure the headers have the right classes - expect($(nameHeader)).to.have['class']('reactable-header-sort-asc'); - }); - }); - - describe('passing `true` to sortable', function () { - var component; - before(function () { - this.render = function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ Name: 'Lee Salminen', Age: '23', Position: 'Programmer' }, { Name: 'Griffin Smith', Age: '18', Position: 'Engineer' }, { Name: 'Ian Zhang', Age: '28', Position: 'Developer' }], - sortable: true }), ReactableTestUtils.testNode()); - }; - - this.render(); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('sorts by the first column in ascending order', function () { - var nameHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.click(nameHeader); - - ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', 'Engineer']); - ReactableTestUtils.expectRowText(1, ['Ian Zhang', '28', 'Developer']); - ReactableTestUtils.expectRowText(2, ['Lee Salminen', '23', 'Programmer']); - - // Make sure the headers have the right classes - expect($(nameHeader)).to.have['class']('reactable-header-sort-asc'); - }); - - it('sorts by the first column in descending order', function () { - var nameHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.click(nameHeader); - - ReactableTestUtils.expectRowText(0, ['Lee Salminen', '23', 'Programmer']); - ReactableTestUtils.expectRowText(1, ['Ian Zhang', '28', 'Developer']); - ReactableTestUtils.expectRowText(2, ['Griffin Smith', '18', 'Engineer']); - - // Make sure the headers have the right classes - expect($(nameHeader)).to.have['class']('reactable-header-sort-desc'); - }); - - it('sorts by the second column in ascending order', function () { - var nameHeader = $('#table thead tr.reactable-column-header th')[1]; - ReactTestUtils.Simulate.click(nameHeader); - - ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', 'Engineer']); - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', 'Programmer']); - ReactableTestUtils.expectRowText(2, ['Ian Zhang', '28', 'Developer']); - - // Make sure the headers have the right classes - expect($(nameHeader)).to.have['class']('reactable-header-sort-asc'); - }); - - it('sorts by the second column in descending order', function () { - var nameHeader = $('#table thead tr.reactable-column-header th')[1]; - ReactTestUtils.Simulate.click(nameHeader); - - ReactableTestUtils.expectRowText(0, ['Ian Zhang', '28', 'Developer']); - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', 'Programmer']); - ReactableTestUtils.expectRowText(2, ['Griffin Smith', '18', 'Engineer']); - - // Make sure the headers have the right classes - expect($(nameHeader)).to.have['class']('reactable-header-sort-desc'); - }); - - it('sorts by the third column in ascending order', function () { - var positionHeader = $('#table thead tr.reactable-column-header th')[2]; - ReactTestUtils.Simulate.click(positionHeader); - - ReactableTestUtils.expectRowText(0, ['Ian Zhang', '28', 'Developer']); - ReactableTestUtils.expectRowText(1, ['Griffin Smith', '18', 'Engineer']); - ReactableTestUtils.expectRowText(2, ['Lee Salminen', '23', 'Programmer']); - - // Make sure the headers have the right classes - expect($(positionHeader)).to.have['class']('reactable-header-sort-asc'); - }); - - it('sorts by the third column in descending order', function () { - var positionHeader = $('#table thead tr.reactable-column-header th')[2]; - ReactTestUtils.Simulate.click(positionHeader); - - ReactableTestUtils.expectRowText(0, ['Lee Salminen', '23', 'Programmer']); - ReactableTestUtils.expectRowText(1, ['Griffin Smith', '18', 'Engineer']); - ReactableTestUtils.expectRowText(2, ['Ian Zhang', '28', 'Developer']); - - // Make sure the headers have the right classes - expect($(positionHeader)).to.have['class']('reactable-header-sort-desc'); - }); - - it('Keeps the same sort after rerendering', function () { - expect(this.render).to.not['throw'](Error); - - ReactableTestUtils.expectRowText(0, ['Lee Salminen', '23', 'Programmer']); - ReactableTestUtils.expectRowText(1, ['Griffin Smith', '18', 'Engineer']); - ReactableTestUtils.expectRowText(2, ['Ian Zhang', '28', 'Developer']); - }); - }); - - describe('default sort', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ Name: 'Lee Salminen', Age: '23', Position: 'Programmer' }, { Name: 'Griffin Smith', Age: '18', Position: 'Engineer' }, { Name: 'Ian Zhang', Age: '28', Position: 'Developer' }], - sortable: [{ - column: 'Name', - sortFunction: function sortFunction(a, b) { - // Sort by last name - var nameA = a.split(' '); - var nameB = b.split(' '); - - return nameA[1].localeCompare(nameB[1]); - } - }, 'Age', 'Position'], - defaultSort: { column: 'Age', direction: 'desc' } }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders all rows sorted by default column age descending', function () { - ReactableTestUtils.expectRowText(0, ['Ian Zhang', '28', 'Developer']); - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', 'Programmer']); - ReactableTestUtils.expectRowText(2, ['Griffin Smith', '18', 'Engineer']); - }); - }); - - describe('sort descending by default flag', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ Name: 'Lee Salminen', Age: '23', Position: 'Programmer' }, { Name: 'Griffin Smith', Age: '18', Position: 'Engineer' }, { Name: 'Ian Zhang', Age: '28', Position: 'Developer' }], - sortable: [{ - column: 'Name', - sortFunction: function sortFunction(a, b) { - // Sort by last name - var nameA = a.split(' '); - var nameB = b.split(' '); - - return nameA[1].localeCompare(nameB[1]); - } - }, 'Age', 'Position'], - defaultSort: { column: 'Age' }, - defaultSortDescending: true }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders all rows sorted by default column age descending', function () { - ReactableTestUtils.expectRowText(0, ['Ian Zhang', '28', 'Developer']); - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', 'Programmer']); - ReactableTestUtils.expectRowText(2, ['Griffin Smith', '18', 'Engineer']); - }); - - it('sorts by the age column in ascending order', function () { - var positionHeader = $('#table thead tr.reactable-column-header th')[1]; - ReactTestUtils.Simulate.click(positionHeader); - - ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', 'Engineer']); - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', 'Programmer']); - ReactableTestUtils.expectRowText(2, ['Ian Zhang', '28', 'Developer']); - - // Make sure the headers have the right classes - expect($(positionHeader)).to.have['class']('reactable-header-sort-asc'); - }); - }); - - describe('default sort no direction specified', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ Name: 'Lee Salminen', Age: '23', Position: 'Programmer' }, { Name: 'Griffin Smith', Age: '18', Position: 'Engineer' }, { Name: 'Ian Zhang', Age: '28', Position: 'Developer' }], - sortable: [{ - column: 'Name', - sortFunction: function sortFunction(a, b) { - // Sort by last name - var nameA = a.split(' '); - var nameB = b.split(' '); - - return nameA[1].localeCompare(nameB[1]); - } - }, 'Age', 'Position'], - defaultSort: 'Age' }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders all rows sorted by default column age ascending', function () { - ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', 'Engineer']); - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', 'Programmer']); - ReactableTestUtils.expectRowText(2, ['Ian Zhang', '28', 'Developer']); - }); - }); - - describe('unsortable column', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ Name: 'Lee Salminen', Age: '23', Position: 'Programmer' }, { Name: 'Griffin Smith', Age: '18', Position: 'Engineer' }, { Name: 'Ian Zhang', Age: '28', Position: 'Developer' }], - sortable: ['Age', 'Position'] }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('leaves columns unsorted', function () { - var nameHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.click(nameHeader); - - ReactableTestUtils.expectRowText(0, ['Lee Salminen', '23', 'Programmer']); - ReactableTestUtils.expectRowText(1, ['Griffin Smith', '18', 'Engineer']); - ReactableTestUtils.expectRowText(2, ['Ian Zhang', '28', 'Developer']); - }); - }); - - [Reactable.Sort.Numeric, Reactable.Sort.NumericInteger].forEach(function (method) { - describe('numeric sort', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ Count: '23' }, { Count: '18' }, { Count: '28' }, { Count: '1.23' }, { Count: 'a' }, { Count: 'z' }, { Count: '123' }], - columns: [{ key: 'Count', sortable: method }] }), ReactableTestUtils.testNode()); - }); - - after(function () { - ReactableTestUtils.resetTestEnvironment(); - }); - - it('sorts columns numerically', function () { - var sortHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.click(sortHeader); - - ReactableTestUtils.expectRowText(0, ['1.23']); - ReactableTestUtils.expectRowText(1, ['18']); - ReactableTestUtils.expectRowText(2, ['23']); - ReactableTestUtils.expectRowText(3, ['28']); - ReactableTestUtils.expectRowText(4, ['123']); - ReactableTestUtils.expectRowText(5, ['a']); - ReactableTestUtils.expectRowText(6, ['z']); - }); - }); - }); - - describe('numeric sort with Tr and Td specified', function () { - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { - className: 'table', - id: 'table', - columns: [{ key: 'Count', sortable: Reactable.Sort.Numeric }] }, - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Count' }, - '23' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Count' }, - '18' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Count' }, - '28' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Count' }, - '1.23' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Count' }, - 'a' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Count' }, - 'z' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Count' }, - '123' - ) - ) - ), ReactableTestUtils.testNode()); - }); - - after(function () { - ReactableTestUtils.resetTestEnvironment(); - }); - - it('sorts columns numerically', function () { - var sortHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.click(sortHeader); - - ReactableTestUtils.expectRowText(0, ['1.23']); - ReactableTestUtils.expectRowText(1, ['18']); - ReactableTestUtils.expectRowText(2, ['23']); - ReactableTestUtils.expectRowText(3, ['28']); - ReactableTestUtils.expectRowText(4, ['123']); - ReactableTestUtils.expectRowText(5, ['a']); - ReactableTestUtils.expectRowText(6, ['z']); - }); - }); - - describe('numeric sort with Tr and Td specified and custom value', function () { - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { - className: 'table', - id: 'table', - columns: [{ key: 'Count', sortable: Reactable.Sort.Numeric }] }, - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Count', value: 23 }, - 'twenty-three' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Count', value: 18 }, - 'eighteen' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Count', value: 28 }, - 'twenty-eight' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Count', value: 1.23 }, - 'one point two three' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Count', value: 'a' }, - 'a' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Count', value: 'z' }, - 'z' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Count', value: 123 }, - 'one hundred twenty-three' - ) - ) - ), ReactableTestUtils.testNode()); - }); - - after(function () { - ReactableTestUtils.resetTestEnvironment(); - }); - - it('sorts columns numerically', function () { - var sortHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.click(sortHeader); - - ReactableTestUtils.expectRowText(0, ['one point two three']); - ReactableTestUtils.expectRowText(1, ['eighteen']); - ReactableTestUtils.expectRowText(2, ['twenty-three']); - ReactableTestUtils.expectRowText(3, ['twenty-eight']); - ReactableTestUtils.expectRowText(4, ['one hundred twenty-three']); - ReactableTestUtils.expectRowText(5, ['a']); - ReactableTestUtils.expectRowText(6, ['z']); - }); - }); - - describe('currency sort', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ Price: '1.25' }, { Price: '$1.01' }, { Price: '1' }, { Price: '$10,000' }, { Price: '$10,500' }, { Price: '$10' }, { Price: 'a' }, { Price: 'z' }, { Price: '$2' }, { Price: '$.5' }, { Price: '$0.60' }, { Price: '.1' }], - columns: [{ key: 'Price', sortable: Reactable.Sort.Currency }] }), ReactableTestUtils.testNode()); - }); - - after(function () { - ReactableTestUtils.resetTestEnvironment(); - }); - - it('sorts columns numerically. parsing out currency symbols', function () { - var sortHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.click(sortHeader); - - ReactableTestUtils.expectRowText(0, ['.1']); - ReactableTestUtils.expectRowText(1, ['$.5']); - ReactableTestUtils.expectRowText(2, ['$0.60']); - ReactableTestUtils.expectRowText(3, ['1']); - ReactableTestUtils.expectRowText(4, ['$1.01']); - ReactableTestUtils.expectRowText(5, ['1.25']); - ReactableTestUtils.expectRowText(6, ['$2']); - ReactableTestUtils.expectRowText(7, ['$10']); - ReactableTestUtils.expectRowText(8, ['$10,000']); - ReactableTestUtils.expectRowText(9, ['$10,500']); - ReactableTestUtils.expectRowText(10, ['a']); - ReactableTestUtils.expectRowText(11, ['z']); - }); - }); - - describe('date sort', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ 'Date': '1/1/2014 11:00 AM' }, { 'Date': '1/1/2013 11:00 AM' }, { 'Date': '1/1/2014 4:30 PM' }, { 'Date': '4/3/2013' }, { 'Date': 'a' }, { 'Date': 'z' }], - columns: [{ key: 'Date', sortable: Reactable.Sort.Date }] }), ReactableTestUtils.testNode()); - }); - - after(function () { - ReactableTestUtils.resetTestEnvironment(); - }); - - it('sorts columns by date', function () { - var sortHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.click(sortHeader); - - ReactableTestUtils.expectRowText(0, ['1/1/2013 11:00 AM']); - ReactableTestUtils.expectRowText(1, ['4/3/2013']); - ReactableTestUtils.expectRowText(2, ['1/1/2014 11:00 AM']); - ReactableTestUtils.expectRowText(3, ['1/1/2014 4:30 PM']); - ReactableTestUtils.expectRowText(4, ['a']); - ReactableTestUtils.expectRowText(5, ['z']); - }); - }); - - describe('case insensitive sorting', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ 'Name': 'Lee Salminen' }, { 'Name': 'Griffin Smith' }, { 'Name': 'Ian Zhang' }, { 'Name': 'lee Salminen' }, { 'Name': 'griffin smith' }, { 'Name': 'Ian zhang' }], - columns: [{ key: 'Name', sortable: Reactable.Sort.CaseInsensitive }] }), ReactableTestUtils.testNode()); - }); - - after(function () { - ReactableTestUtils.resetTestEnvironment(); - }); - - it('sorts columns by value - case insensitive', function () { - var sortHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.click(sortHeader); - - ReactableTestUtils.expectRowText(0, ['Griffin Smith']); - ReactableTestUtils.expectRowText(1, ['griffin smith']); - ReactableTestUtils.expectRowText(2, ['Ian Zhang']); - ReactableTestUtils.expectRowText(3, ['Ian zhang']); - ReactableTestUtils.expectRowText(4, ['Lee Salminen']); - ReactableTestUtils.expectRowText(5, ['lee Salminen']); - }); - }); - - describe('custom sort with React Components', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ 'Rank': React.createElement( - 'span', - { className: '3' }, - 'Third' - ) }, { 'Rank': React.createElement( - 'span', - { className: '1' }, - 'First' - ) }, { 'Rank': React.createElement( - 'span', - { className: '2' }, - 'Second' - ) }], - columns: [{ - key: 'Rank', sortable: function sortable(a, b) { - // sort based on classname - return a.props.className.localeCompare(b.props.className); - } - }] }), ReactableTestUtils.testNode()); - }); - - after(function () { - ReactableTestUtils.resetTestEnvironment(); - }); - - it('sorts columns by value', function () { - var sortHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.click(sortHeader); - - ReactableTestUtils.expectRowText(0, ['First']); - ReactableTestUtils.expectRowText(1, ['Second']); - ReactableTestUtils.expectRowText(2, ['Third']); - }); - }); - - describe('sorts and calls onSort callback via props', function () { - var sortColumn = null; - - var callback = function callback(sortObject) { - sortColumn = sortObject.column; - }; - - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ 'Rank': React.createElement( - 'span', - { className: '3' }, - 'Third' - ) }, { 'Rank': React.createElement( - 'span', - { className: '1' }, - 'First' - ) }, { 'Rank': React.createElement( - 'span', - { className: '2' }, - 'Second' - ) }], - columns: [{ - key: 'Rank', sortable: function sortable(a, b) { - // sort based on classname - return a.props.className.localeCompare(b.props.className); - } - }], - onSort: callback }), ReactableTestUtils.testNode()); - }); - - after(function () { - ReactableTestUtils.resetTestEnvironment(); - }); - - it('returns currentSort object to callback for utilization', function () { - var sortHeader = $('#table thead tr.reactable-column-header th')[0]; - ReactTestUtils.Simulate.click(sortHeader); - - expect(sortColumn).to.equal('Rank'); - }); - }); - }); - - describe('filtering', function () { - describe('filtering with javascript objects for data', function () { - var data = [{ name: "Lee SomeoneElse", age: 18 }, { name: "Lee Salminen", age: 23 }, { name: "No Age", age: null }]; - var filterBy; - var onFilter = function onFilter(filter) { - filterBy = filter; - }; - before(function () { - ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table', - filterable: ['Name', 'Age'], - onFilter: onFilter }, - React.createElement( - Reactable.Tr, - null, - React.createElement(Reactable.Td, { column: 'Name', data: data[0].name }), - React.createElement(Reactable.Td, { column: 'Age', data: data[0].age }) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement(Reactable.Td, { column: 'Name', data: data[1].name }), - React.createElement(Reactable.Td, { column: 'Age', data: data[1].age }) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement(Reactable.Td, { column: 'Name', data: data[2].name }), - React.createElement(Reactable.Td, { column: 'Age', data: data[2].age }) - ) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('filters case insensitive on specified columns', function () { - var $filter = $('#table thead tr.reactable-filterer input.reactable-filter-input'); - - $filter.val('lee'); - React.addons.TestUtils.Simulate.keyUp($filter[0]); - - ReactableTestUtils.expectRowText(0, ['Lee SomeoneElse', '18']); - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23']); - }); - it('calls onFilter event handler', function () { - var $filter = $('#table thead tr.reactable-filterer input.reactable-filter-input'); - var textToSearch = 'lee'; - - $filter.val(textToSearch); - React.addons.TestUtils.Simulate.keyUp($filter[0]); - - expect(filterBy).to.equal(textToSearch); - }); - }); - - describe('basic case-insensitive filtering', function () { - before(function () { - this.component = ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table', - filterable: ['State', 'Tag'], - filterPlaceholder: 'Filter Results', - columns: ['State', 'Description', 'Tag'] }, - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'State' }, - 'New York' - ), - React.createElement( - Reactable.Td, - { column: 'Description' }, - 'this is some text' - ), - React.createElement( - Reactable.Td, - { column: 'Tag' }, - 'new' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'State' }, - 'New Mexico' - ), - React.createElement( - Reactable.Td, - { column: 'Description' }, - 'lorem ipsum' - ), - React.createElement( - Reactable.Td, - { column: 'Tag' }, - 'old' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'State' }, - 'Colorado' - ), - React.createElement( - Reactable.Td, - { column: 'Description' }, - 'new description that shouldnt match filter' - ), - React.createElement( - Reactable.Td, - { column: 'Tag' }, - 'old' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'State' }, - 'Alaska' - ), - React.createElement( - Reactable.Td, - { column: 'Description' }, - 'bacon' - ), - React.createElement( - Reactable.Td, - { column: 'Tag' }, - 'renewed' - ) - ) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - context('from the filterer field', function () { - it('filters case insensitive on specified columns', function () { - var $filter = $('#table thead tr.reactable-filterer input.reactable-filter-input'); - - $filter.val('new'); - React.addons.TestUtils.Simulate.keyUp($filter[0]); - - ReactableTestUtils.expectRowText(0, ['New York', 'this is some text', 'new']); - ReactableTestUtils.expectRowText(1, ['New Mexico', 'lorem ipsum', 'old']); - ReactableTestUtils.expectRowText(2, ['Alaska', 'bacon', 'renewed']); - }); - - it('filter placeholder is set', function () { - var $filter = $('#table thead tr.reactable-filterer input.reactable-filter-input'); - expect($filter.attr("placeholder")).to.equal('Filter Results'); - }); - }); - - context('from the function', function () { - before(function () { - this.component.filterBy('york'); - }); - - it('applies the filtering', function () { - ReactableTestUtils.expectRowText(0, ['New York', 'this is some text', 'new']); - }); - - it('updates the value of the filterer', function () { - var $filter = $('#table thead tr.reactable-filterer input.reactable-filter-input'); - expect($filter).to.have.value('york'); - }); - }); - - context('from filterBy prop', function () { - before(function () { - ReactableTestUtils.resetTestEnvironment(); - - var ParentComponent = React.createClass({ - displayName: 'ParentComponent', - - getInitialState: function getInitialState() { - return { customFilterText: 'new' }; - }, - - handleChange: function handleChange(event) { - this.setState({ customFilterText: event.target.value }); - }, - - render: function render() { - return React.createElement( - 'div', - null, - React.createElement('input', { type: 'text', ref: 'customFilterInput', id: 'customFilterInput', value: this.state.customFilterText, onChange: this.handleChange }), - React.createElement( - Reactable.Table, - { className: 'table', id: 'table', - filterable: ['State', 'Tag'], - filterPlaceholder: 'Filter Results', - filterBy: this.state.customFilterText, - columns: ['State', 'Description', 'Tag'] }, - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'State' }, - 'New York' - ), - React.createElement( - Reactable.Td, - { column: 'Description' }, - 'this is some text' - ), - React.createElement( - Reactable.Td, - { column: 'Tag' }, - 'new' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'State' }, - 'New Mexico' - ), - React.createElement( - Reactable.Td, - { column: 'Description' }, - 'lorem ipsum' - ), - React.createElement( - Reactable.Td, - { column: 'Tag' }, - 'old' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'State' }, - 'Colorado' - ), - React.createElement( - Reactable.Td, - { column: 'Description' }, - 'new description that shouldnt match filter' - ), - React.createElement( - Reactable.Td, - { column: 'Tag' }, - 'old' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'State' }, - 'Alaska' - ), - React.createElement( - Reactable.Td, - { column: 'Description' }, - 'bacon' - ), - React.createElement( - Reactable.Td, - { column: 'Tag' }, - 'renewed' - ) - ) - ) - ); - } - }); - - this.component = ReactDOM.render(React.createElement(ParentComponent), ReactableTestUtils.testNode()); - }); - - it('filters case insensitive on specified columns', function () { - ReactableTestUtils.expectRowText(0, ['New York', 'this is some text', 'new']); - ReactableTestUtils.expectRowText(1, ['New Mexico', 'lorem ipsum', 'old']); - ReactableTestUtils.expectRowText(2, ['Alaska', 'bacon', 'renewed']); - var $builtInFilter = $('#table thead tr.reactable-filterer input.reactable-filter-input'); - expect($builtInFilter).to.have.value('new'); - - // Simulate changing input on parent component and re-rendering Reactable.Table with new props. - var node = this.component.refs.customFilterInput; - node.value = 'alaska'; - ReactTestUtils.Simulate.change(customFilterInput); - - ReactableTestUtils.expectRowText(0, ['Alaska', 'bacon', 'renewed']); - expect($builtInFilter).to.have.value('alaska'); - }); - }); - }); - - context('filtering with prop and hiding filter input', function () { - before(function () { - this.component = ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table', - filterable: ['State', 'Tag'], - filterPlaceholder: 'Filter Results', - filterBy: 'new', - hideFilterInput: true, - columns: ['State', 'Description', 'Tag'] }, - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'State' }, - 'New York' - ), - React.createElement( - Reactable.Td, - { column: 'Description' }, - 'this is some text' - ), - React.createElement( - Reactable.Td, - { column: 'Tag' }, - 'new' - ) - ), - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'State' }, - 'New Mexico' - ), - React.createElement( - Reactable.Td, - { column: 'Description' }, - 'lorem ipsum' - ), - React.createElement( - Reactable.Td, - { column: 'Tag' }, - 'old' - ) - ) - ), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('does not render the filter input box', function () { - expect($('#table thead tr.reactable-filterer input.reactable-filter-input').length).to.equal(0); - }); - }); - - context('filtering and pagination together', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ 'State': 'New York', 'Description': 'this is some text', 'Tag': 'new' }, { 'State': 'New Mexico', 'Description': 'lorem ipsum', 'Tag': 'old' }, { 'State': 'Colorado', 'Description': 'new description that shouldn\'t match filter', - 'Tag': 'old' }, { 'State': 'Alaska', 'Description': 'bacon', 'Tag': 'renewed' }], - filterable: ['State', 'Tag'], - columns: ['State', 'Description', 'Tag'], - itemsPerPage: 2 }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - afterEach(function () { - var $filter = $('#table thead tr.reactable-filterer input.reactable-filter-input'); - $filter.val(''); - React.addons.TestUtils.Simulate.keyUp($filter[0]); - }); - - it('updates the pagination links', function () { - var $filter = $('#table thead tr.reactable-filterer input.reactable-filter-input'); - - $filter.val('colorado'); - React.addons.TestUtils.Simulate.keyUp($filter[0]); - - var pageButtons = $('#table tbody.reactable-pagination a.reactable-page-button'); - expect(pageButtons.length).to.equal(1); - expect($(pageButtons[0])).to.have.text('1'); - }); - - it('updates the current page if necessary', function () { - var $filter = $('#table thead tr.reactable-filterer input.reactable-filter-input'); - var $pageButtons = $('#table tbody.reactable-pagination a.reactable-page-button'); - - // Go to the last page - React.addons.TestUtils.Simulate.click($pageButtons[1]); - - // Then filter so that that page doesn't exist anymore - $filter.val('colorado'); - React.addons.TestUtils.Simulate.keyUp($filter[0]); - - ReactableTestUtils.expectRowText(0, ['Colorado', "new description that shouldn't match filter", 'old']); - var activePage = $('#table tbody.reactable-pagination ' + 'a.reactable-page-button.reactable-current-page'); - expect(activePage.length).to.equal(1); - expect(activePage).to.have.text('1'); - }); - }); - }); - - describe('directly passing a data array with non-string data', function () { - before(function () { - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table', data: [{ Name: 'Griffin Smith', Age: 18 }, { Age: 23, Name: { toString: function toString() { - return 'Lee Salminen'; - } } }, { Age: 28.45, Position: 'Developer' }] }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('renders the table', function () { - expect($('table#table.table')).to.exist; - }); - - it('renders the column headers in the table', function () { - var headers = []; - $('thead th').each(function () { - headers.push($(this).text()); - }); - - expect(headers).to.eql(['Name', 'Age', 'Position']); - }); - - it('renders the first row with the correct data', function () { - ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', '']); - }); - - it('renders the second row with the correct data', function () { - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', '']); - }); - - it('renders the third row with the correct data', function () { - ReactableTestUtils.expectRowText(2, ['', '28.45', 'Developer']); - }); - }); - - describe('multiple tables on a page', function () { - before(function () { - this.parentTestNode = ReactableTestUtils.testNode(); - this.testNode1 = $('
').attr('id', 'test-node-1'); - this.testNode2 = $('
').attr('id', 'test-node-2'); - - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table1', data: [{ Name: 'Griffin Smith', Age: '18' }, { Age: '23', Name: 'Lee Salminen' }, { Age: '28', Position: 'Developer' }] }), this.testNode1[0]); - - ReactDOM.render(React.createElement(Reactable.Table, { className: 'table', id: 'table2', data: [{ Moniker: 'Griffin Smith', Elderliness: '18' }, { Elderliness: '23', Moniker: 'Lee Salminen' }, { Elderliness: '28', Title: 'Developer' }] }), this.testNode2[0]); - }); - - after(function () { - $(this.parentTestNode).empty().remove(); - }); - - it('renders the column headers in the first table', function () { - var headers = []; - this.testNode1.find('thead th').each(function () { - headers.push($(this).text()); - }); - - expect(headers).to.eql(['Name', 'Age', 'Position']); - }); - - it('renders the column headers in the second table', function () { - var headers = []; - this.testNode2.find('thead th').each(function () { - headers.push($(this).text()); - }); - - expect(headers).to.eql(['Moniker', 'Elderliness', 'Title']); - }); - }); - - describe('handleClick callbacks', function () { - before(function () { - this.clicked = false; - - ReactDOM.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table' }, - React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name', handleClick: (function () { - this.clicked = true; - }).bind(this) }, - React.createElement( - 'b', - null, - 'Griffin Smith' - ) - ) - ) - ), ReactableTestUtils.testNode()); - - ReactTestUtils.Simulate.click($('td')[0]); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('calls the callbacks on click', function () { - expect(this.clicked).to.eq(true); - }); - }); - - describe('table with no data', function () { - context('when noDataText prop is null', function () { - before(function () { - this.component = ReactDOM.render(React.createElement(Reactable.Table, { data: [], columns: ['State', 'Description', 'Tag'] }), ReactableTestUtils.testNode()); - }); - - after(ReactableTestUtils.resetTestEnvironment); - - it('does not render the reactable-no-data element', function () { - expect($('.reactable-no-data').length).to.eq(0); - }); - }); - - context('when initialized without
',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table'},React.createElement(Reactable.Tr,{data:{Name:'Griffin Smith',Age:'18'}}),React.createElement(Reactable.Tr,{data:{Age:'23',Name:'Lee Salminen'}}),React.createElement(Reactable.Tr,{data:{Age:'28',Position:'Developer'}})),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the table',function(){expect($('table#table.table')).to.exist;});it('renders the column headers in the table',function(){var headers=[];$('thead th').each(function(){headers.push($(this).text());});expect(headers).to.eql(['Name','Age','Position']);});it('renders the first row with the correct data',function(){ReactableTestUtils.expectRowText(0,['Griffin Smith','18','']);});it('renders the second row with the correct data',function(){ReactableTestUtils.expectRowText(1,['Lee Salminen','23','']);});it('renders the third row with the correct data',function(){ReactableTestUtils.expectRowText(2,['','28','Developer']);});});describe('adding s',function(){context('with only one s',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table'},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name'},React.createElement('b',null,'Griffin Smith')),React.createElement(Reactable.Td,{column:'Age'},React.createElement('em',null,'18'))),null,React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Position'},React.createElement('b',null,'Developer')),React.createElement(Reactable.Td,{column:'Age'},React.createElement('em',null,'28')))),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the table',function(){expect($('table#table.table')).to.exist;});it('renders the column headers in the table',function(){var headers=[];$('thead th').each(function(){headers.push($(this).text());});expect(headers).to.eql(['Name','Age','Position']);});it('renders the first row with the correct data',function(){ReactableTestUtils.expectRowText(0,['Griffin Smith','18','']);});it('renders the second row with the correct data',function(){ReactableTestUtils.expectRowText(1,['','28','Developer']);});});});describe('Adding a ',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',sortable:['Name'],filterable:['Name','Age']},React.createElement(Reactable.Tr,{className:'rowClass1',data:{Name:'Griffin Smith',Age:'18'}}),React.createElement(Reactable.Tr,{className:'rowClass2',data:{Age:'23',Name:'Lee Salminen'}}),React.createElement(Reactable.Tr,{className:'rowClass3',data:{Age:'28',Position:'Developer'}}),React.createElement(Reactable.Tfoot,{id:'tfoot'},React.createElement('tr',null,React.createElement('td',{id:'tfoot-stuff'},'Test')))),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the table',function(){expect($('#table')).to.exist;});it('renders the regular data rows',function(){ReactableTestUtils.expectRowText(0,['Griffin Smith','18','']);ReactableTestUtils.expectRowText(1,['Lee Salminen','23','']);ReactableTestUtils.expectRowText(2,['','28','Developer']);});it('renders the tfoot',function(){expect($('#tfoot')).to.exist;});it('renders the children of the tfoot',function(){expect($('#tfoot-stuff')).to.exist;});context('when sorting',function(){before(function(){ReactTestUtils.Simulate.click($('th')[0]);});it('leaves the tfoot alone',function(){expect($('table :last-child')).to.match('tfoot');});});context('when filtering',function(){before(function(){var $filter=$('.reactable-filter-input');$filter.val('griffin');ReactTestUtils.Simulate.keyUp($filter[0]);});it('leaves the tfoot alone',function(){expect($('table :last-child')).to.match('tfoot');});});});describe('passing through HTML props',function(){describe('adding s with className to the
s to the
',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table'},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name'},'Griffin Smith')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name'},'Lee Salminen')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name'},'Ian Zhang'))),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the table',function(){expect($('table#table.table')).to.exist;});it('renders the column headers in the table',function(){var headers=[];$('thead th').each(function(){headers.push($(this).text());});expect(headers).to.eql(['Name']);});it('renders the first row with the correct data',function(){ReactableTestUtils.expectRowText(0,['Griffin Smith']);});it('renders the second row with the correct data',function(){ReactableTestUtils.expectRowText(1,['Lee Salminen']);});it('renders the third row with the correct data',function(){ReactableTestUtils.expectRowText(2,['Ian Zhang']);});});context('with multiple s',function(){context('with plain text',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table'},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name'},'Griffin Smith'),React.createElement(Reactable.Td,{column:'Age'},'18')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name'},'Lee Salminen'),React.createElement(Reactable.Td,{column:'Age'},'23')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Position'},'Developer'),React.createElement(Reactable.Td,{column:'Age'},'28'))),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the table',function(){expect($('table#table.table')).to.exist;});it('renders the column headers in the table',function(){var headers=[];$('thead th').each(function(){headers.push($(this).text());});expect(headers).to.eql(['Name','Age','Position']);});it('renders the first row with the correct data',function(){ReactableTestUtils.expectRowText(0,['Griffin Smith','18','']);});it('renders the second row with the correct data',function(){ReactableTestUtils.expectRowText(1,['Lee Salminen','23','']);});it('renders the third row with the correct data',function(){ReactableTestUtils.expectRowText(2,['','28','Developer']);});});});context('with React.DOM nodes inside',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table'},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name'},React.createElement('b',null,'Griffin Smith')),React.createElement(Reactable.Td,{column:'Age'},React.createElement('em',null,'18'))),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name'},React.createElement('b',null,'Lee Salminen')),React.createElement(Reactable.Td,{column:'Age'},React.createElement('em',null,'23'))),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Position'},React.createElement('b',null,'Developer')),React.createElement(Reactable.Td,{column:'Age'},React.createElement('em',null,'28')))),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the table',function(){expect($('table#table.table')).to.exist;});it('renders the column headers in the table',function(){var headers=[];$('thead th').each(function(){headers.push($(this).text());});expect(headers).to.eql(['Name','Age','Position']);});it('renders the first row with the correct data',function(){ReactableTestUtils.expectRowText(0,['Griffin Smith','18','']);});it('renders the second row with the correct data',function(){ReactableTestUtils.expectRowText(1,['Lee Salminen','23','']);});it('renders the third row with the correct data',function(){ReactableTestUtils.expectRowText(2,['','28','Developer']);});});context('with null s',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table'},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name'},React.createElement('b',null,'Griffin Smith')),null),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name'},React.createElement('b',null,'Lee Salminen')),React.createElement(Reactable.Td,{column:'Age'},React.createElement('em',null,'23'))),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Position'},React.createElement('b',null,'Developer')),React.createElement(Reactable.Td,{column:'Age'},React.createElement('em',null,'28')))),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the table',function(){expect($('table#table.table')).to.exist;});it('renders the column headers in the table',function(){var headers=[];$('thead th').each(function(){headers.push($(this).text());});expect(headers).to.eql(['Name','Age','Position']);});it('renders the first row with the correct data',function(){ReactableTestUtils.expectRowText(0,['Griffin Smith','','']);});it('renders the second row with the correct data',function(){ReactableTestUtils.expectRowText(1,['Lee Salminen','23','']);});it('renders the third row with the correct data',function(){ReactableTestUtils.expectRowText(2,['','28','Developer']);});});context('with null
',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table'},React.createElement(Reactable.Tr,{className:'rowClass1',data:{Name:'Griffin Smith',Age:'18'}}),React.createElement(Reactable.Tr,{className:'rowClass2',data:{Age:'23',Name:'Lee Salminen'}}),React.createElement(Reactable.Tr,{className:'rowClass3',data:{Age:'28',Position:'Developer'}})),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the table',function(){expect($('table#table.table')).to.exist;});it('renders the column headers in the table',function(){var headers=[];$('thead th').each(function(){headers.push($(this).text());});expect(headers).to.eql(['Name','Age','Position']);});it('renders the first row with the correct class name',function(){ReactableTestUtils.expectRowClass(0,'rowClass1');});it('renders the second row with the correct class name',function(){ReactableTestUtils.expectRowClass(1,'rowClass2');});it('renders the third row with the correct class name',function(){ReactableTestUtils.expectRowClass(2,'rowClass3');});});describe('adding - {this.props.filtering === true ? + this.domEl = el)}}> + {!topPagination && filtering && : null} + colSpan={columns.length} + onFilter={onFilter} + placeholder={filterPlaceholder} + value={currentFilter} + /> + } + {topPagination ? + + + : null + } {Ths} ); diff --git a/tests/reactable_test.jsx b/tests/reactable_test.jsx index 7d00ae54..98162120 100644 --- a/tests/reactable_test.jsx +++ b/tests/reactable_test.jsx @@ -2232,4 +2232,83 @@ describe('Reactable', function() { }); }); }) + + describe('visibleItems()', () => { + const {Table, Thead, Tr, Th, Td} = Reactable; + const data = [{name:"Lee SomeoneElse", age:18},{name:"Lee Salminen", age:23},{name:"No Age", age:null}]; + class Tester extends React.Component { + componentDidMount() { + this.props.run && this.props.run(this) + } + render() { + return ( +
s with classNames to the ',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table'},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name',className:'name-1'},'Griffin Smith'),React.createElement(Reactable.Td,{column:'Age'},'18')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name',className:'name-2'},'Lee Salminen'),React.createElement(Reactable.Td,{column:'Age'},'23')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Position',className:'position'},'Developer'),React.createElement(Reactable.Td,{column:'Age'},'28'))),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the first column with the correct class name',function(){expect($('td.name-1')).to.have.text('Griffin Smith');});it('renders the second column with the correct class name',function(){expect($('td.name-2')).to.have.text('Lee Salminen');});it('renders the third column with the correct class name',function(){expect($('td.position')).to.have.text('Developer');});});});describe('adding
with style to the ',function(){before(function(){var tdStyle={width:"100px"};ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table'},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name',className:'name-1',style:tdStyle},'Griffin Smith'),React.createElement(Reactable.Td,{column:'Age'},'18'))),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the first column with the width',function(){expect($('td.name-1')).to.have.attr('style').match(/width/);});});describe('specifying an array of columns',function(){describe('as strings',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{Name:'Griffin Smith',Age:'18',HideThis:'one'},{Age:'23',Name:'Lee Salminen',HideThis:'two'},{Age:'28',Position:'Developer'}],columns:['Name','Age']}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('omits columns not in the list',function(){var columns=$('tr.reactable-column-header th');expect(columns.length).to.equal(2);expect($(columns[0])).to.have.text('Name');expect($(columns[1])).to.have.text('Age');});it('adds class name for each column base on its label',function(){var columns=$('tr.reactable-column-header th');expect($(columns[0])).to.have['class']('reactable-th-name');expect($(columns[1])).to.have['class']('reactable-th-age');});});describe('as objects',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{name:'Griffin Smith',age:'18',HideThis:'one'},{age:'23',name:'Lee Salminen',HideThis:'two'},{age:'28',Position:'Developer'}],columns:[{key:'name',label:'Name'},{key:'age',label:'Age'}]}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('omits columns not in the list',function(){var columns=$('tr.reactable-column-header th');expect(columns.length).to.equal(2);});it('allows changing the labels of the columns',function(){var columns=$('tr.reactable-column-header th');expect($(columns[0])).to.have.text('Name');expect($(columns[1])).to.have.text('Age');});it('adds class name for each column base on its key',function(){var columns=$('tr.reactable-column-header th');expect($(columns[0])).to.have['class']('reactable-th-name');expect($(columns[1])).to.have['class']('reactable-th-age');});});});describe('specifying columns using a ',function(){describe('and an element for the column title',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{id:'table',data:[{Name:Reactable.unsafe('Griffin Smith'),Age:'18'},{Age:'28',Position:Reactable.unsafe('Developer')},{Age:'23',Name:Reactable.unsafe('Lee Salminen')}]},React.createElement(Reactable.Thead,null,React.createElement(Reactable.Th,{column:'Name',id:'my-name'},React.createElement('strong',null,'name')))),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders only the columns in the Thead',function(){expect($('#table tbody tr:first td')).to.exist;expect($('#table thead tr:first th')).to.exist;});it('renders the contents of the Th',function(){expect($('#table>thead>tr>th>strong')).to.exist;});it('passes through the properties of the Th',function(){expect($('#table>thead>tr>th')).to.have.id('my-name');});});describe('and a string for the column title',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{id:'table',data:[{Name:Reactable.unsafe('Griffin Smith'),Age:'18'},{Age:'28',Position:Reactable.unsafe('Developer')},{Age:'23',Name:Reactable.unsafe('Lee Salminen')}]},React.createElement(Reactable.Thead,null,React.createElement(Reactable.Th,{column:'Name',id:'my-name'},'name'))),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders only the columns in the Thead',function(){expect($('#table tbody tr:first td')).to.exist;expect($('#table thead tr:first th')).to.exist;});it('renders the contents of the Th',function(){expect($('#table>thead>tr>th')).to.exist;});it('passes through the properties of the Th',function(){expect($('#table>thead>tr>th')).to.have.id('my-name');});});});describe('unsafe() strings',function(){context('in the
directly',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{Name:Reactable.unsafe('Griffin Smith'),Age:'18'},{Age:'28',Position:Reactable.unsafe('Developer')},{Age:'23',Name:Reactable.unsafe('Lee Salminen')}],sortable:['Name']}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the HTML in the table cells',function(){var griffins_name=$('span#griffins-name');expect(griffins_name.length).to.equal(1);expect(griffins_name).to.have.text('Griffin Smith');var lees_name=$('span#lees-name');expect(lees_name.length).to.equal(1);expect(lees_name).to.have.text('Lee Salminen');var who_knows_job=$('span#who-knows-job');expect(who_knows_job.length).to.equal(1);expect(who_knows_job).to.have.text('Developer');});it('still allows sorting',function(){var nameHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.click(nameHeader);ReactableTestUtils.expectRowText(0,['28','Developer','']);ReactableTestUtils.expectRowText(1,['18','','Griffin Smith']);ReactableTestUtils.expectRowText(2,['23','','Lee Salminen']);});});context('in column labels',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{Name:'Griffin Smith',Age:'18'},{Age:'23',Name:'Lee Salminen'},{Age:'28',Position:'Developer'}],columns:[{key:'Name',label:Reactable.unsafe('Name')},{key:'Age',label:Reactable.unsafe('Age')},{key:'Position',label:Reactable.unsafe('Position')}]}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the HTML in the column headers',function(){var headers=[];$('thead th').each(function(){headers.push($(this).html());});expect(headers).to.eql(['Name','Age','Position']);});});context('in the s',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table'},React.createElement(Reactable.Tr,{data:{Name:Reactable.unsafe('Griffin Smith'),Age:'18'}}),React.createElement(Reactable.Tr,{data:{Age:'23',Name:Reactable.unsafe('Lee Salminen')}}),React.createElement(Reactable.Tr,{data:{Age:'28',Position:Reactable.unsafe('Developer')}})),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the HTML in the table cells',function(){var griffins_name=$('span#griffins-name');expect(griffins_name.length).to.equal(1);expect(griffins_name).to.have.text('Griffin Smith');var lees_name=$('span#lees-name');expect(lees_name.length).to.equal(1);expect(lees_name).to.have.text('Lee Salminen');var who_knows_job=$('span#who-knows-job');expect(who_knows_job.length).to.equal(1);expect(who_knows_job).to.have.text('Developer');});});context('in the s',function(){before(function(){this.component = ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',columns:['State','Description','Tag'],noDataText:'No matching records found.'}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('shows the "no data" message',function(){var $text=$('.reactable-no-data').text();expect($text).to.eq('No matching records found.');});});context('when filtered without any matches',function(){before(function(){this.component = ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',filterable:['State','Tag'],filterPlaceholder:'Filter Results',filterBy:'xxxxx',noDataText:'No matching records found.',columns:['State','Description','Tag']},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'State'},'New York'),React.createElement(Reactable.Td,{column:'Description'},'this is some text'),React.createElement(Reactable.Td,{column:'Tag'},'new')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'State'},'New Mexico'),React.createElement(Reactable.Td,{column:'Description'},'lorem ipsum'),React.createElement(Reactable.Td,{column:'Tag'},'old')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'State'},'Colorado'),React.createElement(Reactable.Td,{column:'Description'},'new description that shouldnt match filter'),React.createElement(Reactable.Td,{column:'Tag'},'old')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'State'},'Alaska'),React.createElement(Reactable.Td,{column:'Description'},'bacon'),React.createElement(Reactable.Td,{column:'Tag'},'renewed'))),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('shows the "no data" message',function(){var text=$('.reactable-no-data').text();expect(text).to.eq('No matching records found.');});});context('when initialized with an empty array for `data` prop',function(){before(function(){this.component = ReactDOM.render(React.createElement(Reactable.Table,{data:[],className:'table',id:'table',columns:['State','Description','Tag'],noDataText:'No matching records found.'}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('shows the "no data" message',function(){var $text=$('.reactable-no-data').text();expect($text).to.eq('No matching records found.');});});});describe('visibleItems()',function(){var _Reactable=Reactable;var Table=_Reactable.Table;var Thead=_Reactable.Thead;var Tr=_Reactable.Tr;var Th=_Reactable.Th;var Td=_Reactable.Td;var data=[{name:"Lee SomeoneElse",age:18},{name:"Lee Salminen",age:23},{name:"No Age",age:null}];var Tester=(function(_React$Component){_inherits(Tester,_React$Component);function Tester(){_classCallCheck(this,Tester);_get(Object.getPrototypeOf(Tester.prototype),'constructor',this).apply(this,arguments);}_createClass(Tester,[{key:'componentDidMount',value:function componentDidMount(){this.props.run && this.props.run(this);}},{key:'render',value:function render(){return React.createElement(Table,_extends({ref:'table',className:'table',id:'table',filterable:['Name','Age']},this.props),React.createElement(Thead,null,React.createElement(Th,{column:'Name'},'The Name'),React.createElement(Th,{column:'Age'},'The Age')),data.map(function(item,i){return React.createElement(Tr,{key:i,i:i < 2?i:null},React.createElement(Td,{column:'Name',data:item.name}),React.createElement(Td,{column:'Age',data:item.age}));}),React.createElement(Tr,{i:true},React.createElement(Td,{column:'Name',data:"hi"}),React.createElement(Td,{column:'Age',data:5})),React.createElement(Tr,{id:"id"},React.createElement(Td,{column:'Name',data:"hi"}),React.createElement(Td,{column:'Age',data:7})));}}]);return Tester;})(React.Component);var doTest=function doTest(runMe){var options=arguments.length <= 1 || arguments[1] === undefined?{}:arguments[1];return ReactDOM.render(React.createElement(Tester,_extends({run:runMe},options)),ReactableTestUtils.testNode());};after(ReactableTestUtils.resetTestEnvironment);it('works',function(){doTest(function(me){var items=me.refs.table.visibleItems();expect(items.length).to.eql(5);expect(items[0].Age).to.eql(18);});});it('works when filtered',function(){doTest(function(me){var items=me.refs.table.visibleItems();expect(items.length).to.eql(2);expect(items[1].Age).to.eql(23);},{filterBy:'lee'});});it('works when paged',function(){doTest(function(me){var items=me.refs.table.visibleItems();expect(items.length).to.eql(1);expect(items[0].Age).to.eql(18);},{itemsPerPage:1});});it('has keys',function(){doTest(function(me){var items=me.refs.table.visibleItems();expect(items[1].id).to.eql(1); // id is not defined and key is a string +expect(items[2].id).to.eql("2"); // Falsy ids still count +expect(items[0].id).to.eql(0);expect(items[3].id).to.eql(true);expect(items[4].id).to.eql("id");});});});});}); diff --git a/lib/reactable/btnPaginator.js b/lib/reactable/btnPaginator.js new file mode 100644 index 00000000..0e6b2c95 --- /dev/null +++ b/lib/reactable/btnPaginator.js @@ -0,0 +1,138 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +function pageHref(num) { + return "#page-" + (num + 1); +} + +var tr = { + of: { + nl: "van", + fr: "de", + de: "von", + en: "of" + } +}; + +var BtnPaginator = (function (_React$Component) { + _inherits(BtnPaginator, _React$Component); + + function BtnPaginator(props) { + _classCallCheck(this, BtnPaginator); + + _get(Object.getPrototypeOf(BtnPaginator.prototype), "constructor", this).call(this, props); + this.handlePrevious = this.handlePrevious.bind(this); + this.handleNext = this.handleNext.bind(this); + } + + _createClass(BtnPaginator, [{ + key: "handlePrevious", + value: function handlePrevious(e) { + e.preventDefault(); + this.props.onPageChange(this.props.currentPage - 1); + } + }, { + key: "handleNext", + value: function handleNext(e) { + e.preventDefault(); + this.props.onPageChange(this.props.currentPage + 1); + } + }, { + key: "renderPrevious", + value: function renderPrevious() { + var currentPage = this.props.currentPage; + + var canPage = currentPage > 0; + return _react2["default"].createElement("button", { className: "reactable-previous-page " + (canPage ? "" : "disabled"), + href: pageHref(this.props.currentPage - 1), + onClick: canPage && this.handlePrevious }); + } + }, { + key: "renderNext", + value: function renderNext() { + var _props = this.props; + var currentPage = _props.currentPage; + var numPages = _props.numPages; + + var canPage = currentPage + 1 !== numPages; + return _react2["default"].createElement("button", { className: "reactable-next-page " + (canPage ? "" : "disabled"), + href: pageHref(this.props.currentPage + 1), + onClick: canPage && this.handleNext }); + } + }, { + key: "calcRange", + value: function calcRange() { + var _props2 = this.props; + var currentPage = _props2.currentPage; + var itemsNumber = _props2.itemsNumber; + var itemsPerPage = _props2.itemsPerPage; + + var fromVal = currentPage === -1 ? 0 : currentPage * itemsPerPage + 1; + var toValTmp = (currentPage + 1) * itemsPerPage; + var toVal = toValTmp < itemsNumber ? toValTmp : itemsNumber; + return fromVal + " - " + toVal; + } + }, { + key: "renderCounter", + value: function renderCounter() { + var _props3 = this.props; + var itemsNumber = _props3.itemsNumber; + var locale = _props3.locale; + + return _react2["default"].createElement( + "div", + { className: "counter" }, + _react2["default"].createElement( + "span", + null, + this.calcRange(), + " ", + tr.of[locale], + " ", + itemsNumber + ) + ); + } + }, { + key: "render", + value: function render() { + + if (typeof this.props.currentPage === 'undefined') { + throw new TypeError('Must pass a currentPage argument to Paginator'); + } + return _react2["default"].createElement( + "div", + { className: "reactable-pagesSwitcher" }, + this.renderCounter(), + _react2["default"].createElement( + "div", + { className: "reactable-pageBtns" }, + this.renderPrevious(), + this.renderNext() + ) + ); + } + }]); + + return BtnPaginator; +})(_react2["default"].Component); + +exports.BtnPaginator = BtnPaginator; +; diff --git a/lib/reactable/filterer.js b/lib/reactable/filterer.js index b88f7f28..8a053dd8 100644 --- a/lib/reactable/filterer.js +++ b/lib/reactable/filterer.js @@ -1,55 +1,66 @@ -'use strict'; +"use strict"; -Object.defineProperty(exports, '__esModule', { +Object.defineProperty(exports, "__esModule", { value: true }); -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); -var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } -function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _react = require('react'); var _react2 = _interopRequireDefault(_react); -var _reactDom = require('react-dom'); - -var _reactDom2 = _interopRequireDefault(_reactDom); - var FiltererInput = (function (_React$Component) { _inherits(FiltererInput, _React$Component); function FiltererInput() { _classCallCheck(this, FiltererInput); - _get(Object.getPrototypeOf(FiltererInput.prototype), 'constructor', this).apply(this, arguments); + _get(Object.getPrototypeOf(FiltererInput.prototype), "constructor", this).apply(this, arguments); } _createClass(FiltererInput, [{ - key: 'onChange', - value: function onChange() { - this.props.onFilter(_reactDom2['default'].findDOMNode(this).value); - } - }, { - key: 'render', + key: "render", value: function render() { - return _react2['default'].createElement('input', { type: 'text', - className: 'reactable-filter-input', - placeholder: this.props.placeholder, - value: this.props.value, - onKeyUp: this.onChange.bind(this), - onChange: this.onChange.bind(this) }); + var _props = this.props; + var onFilter = _props.onFilter; + var onClean = _props.onClean; + var filterCleanBtn = _props.filterCleanBtn; + var placeholder = _props.placeholder; + var value = _props.value; + + return _react2["default"].createElement( + "div", + { className: "reactable-filter-input-container" }, + _react2["default"].createElement("input", { + className: "reactable-filter-input", + placeholder: placeholder, + value: value, + onChange: function (ev) { + return onFilter(ev.target.value); + } + }), + filterCleanBtn && _react2["default"].createElement( + "div", + { onClick: function () { + return onClean(); + }, className: "reactable-btn-clean" }, + "×" + ) + ); } }]); return FiltererInput; -})(_react2['default'].Component); +})(_react2["default"].Component); exports.FiltererInput = FiltererInput; ; @@ -60,32 +71,36 @@ var Filterer = (function (_React$Component2) { function Filterer() { _classCallCheck(this, Filterer); - _get(Object.getPrototypeOf(Filterer.prototype), 'constructor', this).apply(this, arguments); + _get(Object.getPrototypeOf(Filterer.prototype), "constructor", this).apply(this, arguments); } _createClass(Filterer, [{ - key: 'render', + key: "render", value: function render() { if (typeof this.props.colSpan === 'undefined') { throw new TypeError('Must pass a colSpan argument to Filterer'); } - return _react2['default'].createElement( - 'tr', - { className: 'reactable-filterer' }, - _react2['default'].createElement( - 'td', + return _react2["default"].createElement( + "tr", + { className: "reactable-filterer" }, + _react2["default"].createElement( + "td", { colSpan: this.props.colSpan }, - _react2['default'].createElement(FiltererInput, { onFilter: this.props.onFilter, + _react2["default"].createElement(FiltererInput, { + filterCleanBtn: this.props.filterCleanBtn, + onClean: this.props.onClean, + onFilter: this.props.onFilter, value: this.props.value, - placeholder: this.props.placeholder }) + placeholder: this.props.placeholder + }) ) ); } }]); return Filterer; -})(_react2['default'].Component); +})(_react2["default"].Component); exports.Filterer = Filterer; ; diff --git a/lib/reactable/lib/filter_props_from.js b/lib/reactable/lib/filter_props_from.js index 730e5084..c6952c98 100644 --- a/lib/reactable/lib/filter_props_from.js +++ b/lib/reactable/lib/filter_props_from.js @@ -14,7 +14,23 @@ var internalProps = { itemsPerPage: true, childNode: true, data: true, - children: true + children: true, + topPagination: true, + itemsNumber: true, + numPages: true, + currentPage: true, + topPaginationElem: true, + filtering: true, + onFilter: true, + filterCleanBtn: true, + onClean: true, + onPageChange: true, + filterPlaceholder: true, + currentFilter: true, + sort: true, + sortableColumns: true, + onSort: true, + locale: true }; function filterPropsFrom(baseProps) { diff --git a/lib/reactable/paginator.js b/lib/reactable/paginator.js index 34b90839..8af72425 100644 --- a/lib/reactable/paginator.js +++ b/lib/reactable/paginator.js @@ -18,6 +18,8 @@ var _react = require('react'); var _react2 = _interopRequireDefault(_react); +var _btnPaginator = require('./btnPaginator'); + function pageHref(num) { return '#page-' + (num + 1); } @@ -52,33 +54,46 @@ var Paginator = (function (_React$Component) { }, { key: 'renderPrevious', value: function renderPrevious() { + var previousBtn = { + nl: "Vorige", + fr: "Précédente", + en: "Previous" + }; + var locale = this.props.locale; + if (this.props.currentPage > 0) { return _react2['default'].createElement( 'a', { className: 'reactable-previous-page', href: pageHref(this.props.currentPage - 1), onClick: this.handlePrevious.bind(this) }, - 'Previous' + previousBtn[locale] || previousBtn.en ); } } }, { key: 'renderNext', value: function renderNext() { + var nextBtn = { + nl: "Volgende", + fr: "Suivante", + en: "Next" + }; + var locale = this.props.locale; + if (this.props.currentPage < this.props.numPages - 1) { return _react2['default'].createElement( 'a', { className: 'reactable-next-page', href: pageHref(this.props.currentPage + 1), onClick: this.handleNext.bind(this) }, - 'Next' + nextBtn[locale] || nextBtn.en ); } } }, { key: 'renderPageButton', value: function renderPageButton(className, pageNum) { - return _react2['default'].createElement( 'a', { className: className, @@ -104,9 +119,17 @@ var Paginator = (function (_React$Component) { } var pageButtons = []; - var pageButtonLimit = this.props.pageButtonLimit; - var currentPage = this.props.currentPage; - var numPages = this.props.numPages; + var _props = this.props; + var locale = _props.locale; + var bottomPaginationElem = _props.bottomPaginationElem; + var onPageChange = _props.onPageChange; + var itemsNumber = _props.itemsNumber; + var itemsPerPage = _props.itemsPerPage; + var bottomPagination = _props.bottomPagination; + var pageButtonLimit = _props.pageButtonLimit; + var currentPage = _props.currentPage; + var numPages = _props.numPages; + var lowerHalf = Math.round(pageButtonLimit / 2); var upperHalf = pageButtonLimit - lowerHalf; @@ -131,11 +154,44 @@ var Paginator = (function (_React$Component) { if (numPages - currentPage > upperHalf) { pageButtons.splice(pageButtonLimit, pageButtons.length - pageButtonLimit); } - return _react2['default'].createElement( 'tbody', { className: 'reactable-pagination' }, - _react2['default'].createElement( + bottomPagination ? _react2['default'].createElement( + 'tr', + { className: 'reactable-btnPagination' }, + _react2['default'].createElement( + 'td', + { colSpan: this.props.colSpan }, + _react2['default'].createElement( + 'div', + { className: 'reactable-bottomDesign' }, + _react2['default'].createElement( + 'div', + { className: 'reactable-leftElem' }, + bottomPaginationElem.left + ), + _react2['default'].createElement( + 'div', + { className: 'reactable-mainElem' }, + _react2['default'].createElement(_btnPaginator.BtnPaginator, { + locale: locale, + itemsPerPage: itemsPerPage, + itemsNumber: itemsNumber, + numPages: numPages, + currentPage: currentPage, + onPageChange: onPageChange, + key: 'paginator' + }) + ), + _react2['default'].createElement( + 'div', + { className: 'reactable-rightElem' }, + bottomPaginationElem.right + ) + ) + ) + ) : _react2['default'].createElement( 'tr', null, _react2['default'].createElement( diff --git a/lib/reactable/table.js b/lib/reactable/table.js index bcec32e2..c54f1cf7 100644 --- a/lib/reactable/table.js +++ b/lib/reactable/table.js @@ -123,7 +123,6 @@ var Table = (function (_React$Component) { } else if (typeof descendant.props.children !== 'undefined') { value = descendant.props.children; } else { - console.warn('exports.Td specified without ' + 'a `data` property or children, ' + 'ignoring'); return; } @@ -246,7 +245,7 @@ var Table = (function (_React$Component) { value: function componentWillMount() { this.initialize(this.props); this.sortByCurrentSort(); - this.filterBy(this.props.filterBy); + this.filterBy(this.props.filterBy != null ? this.props.filterBy : this.state.filter); } }, { key: 'componentWillReceiveProps', @@ -254,7 +253,51 @@ var Table = (function (_React$Component) { this.initialize(nextProps); this.updateCurrentSort(nextProps.sortBy); this.sortByCurrentSort(); - this.filterBy(nextProps.filterBy); + this.filterBy(nextProps.filterBy != null ? nextProps.filterBy : this.state.filter); + } + }, { + key: 'componentDidUpdate', + value: function componentDidUpdate() { + this.diffChildren(); + } + }, { + key: 'componentDidMount', + value: function componentDidMount() { + this.diffChildren(); + } + }, { + key: 'diffChildren', + value: function diffChildren() { + var onVisibleChange = this.props.onVisibleChange; + var lastChildren = this.lastChildren; + var lastIds = this.lastIds; + var currentChildren = this.currentChildren; + + if (!onVisibleChange || !currentChildren) { + return; + } + + if (lastChildren === currentChildren) { + return; + } + var currentIds = this.childrenToData(currentChildren, true); + + var same = undefined; + if (lastIds && lastIds.length === currentIds.length) { + same = true; + for (var i = 0; i < currentIds.length; i++) { + if (lastIds[i] !== currentIds[i]) { + same = false; + break; + } + } + } + + this.lastIds = currentIds; + this.lastChildren = currentChildren; + if (!same) { + onVisibleChange(currentIds); + } } }, { key: 'applyFilter', @@ -343,6 +386,42 @@ var Table = (function (_React$Component) { this.props.onSort(currentSort); } } + }, { + key: 'childrenToData', + value: function childrenToData(children, onlyIds) { + return children.map(function (row) { + var _row$props = row.props; + var i = _row$props.i; + var id = _row$props.id; + + var iOrId = i != null ? i : id; + var idOrKey = iOrId != null ? iOrId : row.key; + + if (onlyIds) { + return idOrKey; + } + + var data = { id: idOrKey }; + Object.keys(row.props.data).forEach(function (key) { + var col = row.props.data[key]; + data[key] = col.props.value != null ? col.props.value : col.value; + }); + + return data; + }); + } + }, { + key: 'visibleItems', + value: function visibleItems(onlyIds) { + return this.childrenToData(onlyIds); + } + }, { + key: 'scrollToTop', + value: function scrollToTop() { + if (this.headEl) { + this.headEl.scrollIntoView(); + } + } }, { key: 'render', value: function render() { @@ -417,11 +496,12 @@ var Table = (function (_React$Component) { // Determine if we render the filter box var filtering = false; - if (this.props.filterable && Array.isArray(this.props.filterable) && this.props.filterable.length > 0 && !this.props.hideFilterInput) { + if (Array.isArray(this.props.filterable) && this.props.filterable.length > 0 && !this.props.hideFilterInput) { filtering = true; } // Apply filters + var filterCleanBtn = this.props.filterCleanBtn && this.state.filter; var filteredChildren = children; if (this.state.filter !== '') { filteredChildren = this.applyFilter(this.state.filter, filteredChildren); @@ -430,6 +510,8 @@ var Table = (function (_React$Component) { // Determine pagination properties and which columns to display var itemsPerPage = 0; var pagination = false; + var topPagination = this.props.topPagination || false; + var bottomPagination = this.props.bottomPagination || false; var numPages = undefined; var currentPage = this.state.currentPage; var pageButtonLimit = this.props.pageButtonLimit || 10; @@ -443,7 +525,7 @@ var Table = (function (_React$Component) { currentPage = numPages - 1; } - pagination = true; + pagination = numPages > 1; currentChildren = filteredChildren.slice(currentPage * itemsPerPage, (currentPage + 1) * itemsPerPage); } @@ -460,39 +542,70 @@ var Table = (function (_React$Component) { ) ) : null; + this.currentChildren = currentChildren; return _react2['default'].createElement( 'table', - props, - columns && columns.length > 0 ? _react2['default'].createElement(_thead.Thead, { columns: columns, + null, + columns && columns.length > 0 ? _react2['default'].createElement(_thead.Thead, { + ref: function (t) { + return _this.headEl = t; + }, + columns: columns, + topPagination: topPagination, + itemsNumber: filteredChildren.length, + itemsPerPage: itemsPerPage, + numPages: numPages, + currentPage: currentPage, + topPaginationElem: { + left: this.props.topPaginationElemL, + right: this.props.topPaginationElemR + }, filtering: filtering, onFilter: function (filter) { _this.setState({ filter: filter }); - if (_this.props.onFilter) { - _this.props.onFilter(filter); - } + }, + filterCleanBtn: filterCleanBtn, + onClean: function (filter) { + _this.setState({ filter: '' }); + }, + onPageChange: function (page) { + var onPageChange = _this.props.onPageChange; + + onPageChange && onPageChange(page); + _this.setState({ currentPage: page }); + _this.scrollToTop(); }, filterPlaceholder: this.props.filterPlaceholder, currentFilter: this.state.filter, sort: this.state.currentSort, sortableColumns: this._sortable, onSort: this.onSort.bind(this), - key: 'thead' }) : null, + key: 'thead', + locale: this.props.locale + }) : null, _react2['default'].createElement( 'tbody', { className: 'reactable-data', key: 'tbody' }, currentChildren.length > 0 ? currentChildren : noDataText ), - pagination === true ? _react2['default'].createElement(_paginator.Paginator, { colSpan: columns.length, + pagination ? _react2['default'].createElement(_paginator.Paginator, { bottomPagination: bottomPagination, + itemsNumber: filteredChildren.length, + itemsPerPage: itemsPerPage, + locale: this.props.locale, + colSpan: columns.length, pageButtonLimit: pageButtonLimit, numPages: numPages, currentPage: currentPage, + bottomPaginationElem: { + left: this.props.bottomPaginationElemL, + right: this.props.bottomPaginationElemR + }, onPageChange: function (page) { _this.setState({ currentPage: page }); - if (_this.props.onPageChange) { - _this.props.onPageChange(page); - } + _this.scrollToTop(); }, - key: 'paginator' }) : null, + key: 'paginator' + }) : null, this.tfoot ); } @@ -508,6 +621,6 @@ Table.defaultProps = { defaultSort: false, defaultSortDescending: false, itemsPerPage: 0, - filterBy: '', - hideFilterInput: false + hideFilterInput: false, + locale: 'en' }; diff --git a/lib/reactable/td.js b/lib/reactable/td.js index 7b49fac7..00a07e5e 100644 --- a/lib/reactable/td.js +++ b/lib/reactable/td.js @@ -48,12 +48,14 @@ var Td = (function (_React$Component) { onClick: this.handleClick.bind(this) }; + if (typeof this.props.style !== 'undefined') { + tdProps.style = this.props.style; + } + // Attach any properties on the column to this Td object to allow things like custom event handlers if (typeof this.props.column === 'object') { - for (var key in this.props.column) { - if (key !== 'key' && key !== 'name') { - tdProps[key] = this.props.column[key]; - } + for (var key in this.props.column.props) { + tdProps[key] = this.props.column[key]; } } diff --git a/lib/reactable/thead.js b/lib/reactable/thead.js index 3938aa9b..f657826b 100644 --- a/lib/reactable/thead.js +++ b/lib/reactable/thead.js @@ -26,6 +26,8 @@ var _filterer = require('./filterer'); var _libFilter_props_from = require('./lib/filter_props_from'); +var _btnPaginator = require('./btnPaginator'); + var Thead = (function (_React$Component) { _inherits(Thead, _React$Component); @@ -47,23 +49,51 @@ var Thead = (function (_React$Component) { this.props.onSort(column.key); } } + }, { + key: 'scrollIntoView', + value: function scrollIntoView() { + if (this.domEl && this.domEl.offsetParent === null) { + this.domEl.scrollIntoView(); + } + } }, { key: 'render', value: function render() { + var _this = this; + + var _props = this.props; + var sort = _props.sort; + var sortableColumns = _props.sortableColumns; + var filtering = _props.filtering; + var columns = _props.columns; + var onFilter = _props.onFilter; + var onClean = _props.onClean; + var filterPlaceholder = _props.filterPlaceholder; + var currentFilter = _props.currentFilter; + var topPagination = _props.topPagination; + var filterCleanBtn = _props.filterCleanBtn; + var locale = _props.locale; + var itemsPerPage = _props.itemsPerPage; + var itemsNumber = _props.itemsNumber; + var numPages = _props.numPages; + var currentPage = _props.currentPage; + var onPageChange = _props.onPageChange; + var topPaginationElem = _props.topPaginationElem; + // Declare the list of Ths var Ths = []; - for (var index = 0; index < this.props.columns.length; index++) { - var column = this.props.columns[index]; + for (var index = 0; index < columns.length; index++) { + var column = columns[index]; var thClass = 'reactable-th-' + column.key.replace(/\s+/g, '-').toLowerCase(); var sortClass = ''; - if (this.props.sortableColumns[column.key]) { + if (sortableColumns[column.key]) { sortClass += 'reactable-header-sortable '; } - if (this.props.sort.column === column.key) { + if (sort.column === column.key) { sortClass += 'reactable-header-sort'; - if (this.props.sort.direction === 1) { + if (sort.direction === 1) { sortClass += '-asc'; } else { sortClass += '-desc'; @@ -93,16 +123,59 @@ var Thead = (function (_React$Component) { // Manually transfer props var props = (0, _libFilter_props_from.filterPropsFrom)(this.props); - return _react2['default'].createElement( 'thead', - props, - this.props.filtering === true ? _react2['default'].createElement(_filterer.Filterer, { - colSpan: this.props.columns.length, - onFilter: this.props.onFilter, - placeholder: this.props.filterPlaceholder, - value: this.props.currentFilter - }) : null, + _extends({}, props, { ref: function ref(el) { + return _this.domEl = el; + } }), + !topPagination && filtering && _react2['default'].createElement(_filterer.Filterer, { + colSpan: columns.length, + onFilter: onFilter, + placeholder: filterPlaceholder, + value: currentFilter + }), + topPagination ? _react2['default'].createElement( + 'tr', + { className: 'reactable-btnPagination' }, + _react2['default'].createElement( + 'td', + { colSpan: columns.length }, + _react2['default'].createElement( + 'div', + { className: 'reactable-topDesign' }, + _react2['default'].createElement( + 'div', + { className: 'reactable-leftElem' }, + topPaginationElem.left + ), + _react2['default'].createElement( + 'div', + { className: filtering ? 'reactable-mainElem' : 'reactable-mainElem no-filter' }, + filtering && _react2['default'].createElement(_filterer.FiltererInput, { + filterCleanBtn: filterCleanBtn, + onClean: onClean, + onFilter: onFilter, + placeholder: filterPlaceholder, + value: currentFilter + }), + _react2['default'].createElement(_btnPaginator.BtnPaginator, { + locale: locale, + itemsPerPage: itemsPerPage, + itemsNumber: itemsNumber, + numPages: numPages, + currentPage: currentPage, + onPageChange: onPageChange, + key: 'paginator' + }) + ), + _react2['default'].createElement( + 'div', + { className: 'reactable-rightElem' }, + topPaginationElem.right + ) + ) + ) + ) : null, _react2['default'].createElement( 'tr', { className: 'reactable-column-header' }, @@ -120,20 +193,21 @@ var Thead = (function (_React$Component) { if (typeof th.props !== 'undefined') { column.props = (0, _libFilter_props_from.filterPropsFrom)(th.props); - // use the content as the label & key - if (typeof th.props.children !== 'undefined') { + //set the label depending on the props + if (typeof th.props.label !== 'undefined') { + column.label = th.props.label; + } else if (typeof th.props.children !== 'undefined') { column.label = th.props.children; - column.key = column.label; + } else if (typeof th.props.column === 'string') { + column.label = th.props.column; } // the key in the column attribute supersedes the one defined previously if (typeof th.props.column === 'string') { column.key = th.props.column; - - // in case we don't have a label yet - if (typeof column.label === 'undefined') { - column.label = column.key; - } + } else { + // use the content as the key + column.key = th.props.children; } } diff --git a/src/reactable/btnPaginator.jsx b/src/reactable/btnPaginator.jsx new file mode 100644 index 00000000..d06afce7 --- /dev/null +++ b/src/reactable/btnPaginator.jsx @@ -0,0 +1,80 @@ +import React from 'react'; + +function pageHref(num) { + return `#page-${num + 1}` +} + +const tr = { + of: { + nl: "van", + fr: "de", + de: "von", + en: "of", + } +} + +export class BtnPaginator extends React.Component { + constructor(props) { + super(props); + this.handlePrevious = this.handlePrevious.bind(this); + this.handleNext = this.handleNext.bind(this); + } + handlePrevious(e) { + e.preventDefault(); + this.props.onPageChange(this.props.currentPage - 1) + } + + handleNext(e) { + e.preventDefault(); + this.props.onPageChange(this.props.currentPage + 1); + } + + renderPrevious() { + const {currentPage} = this.props + const canPage = currentPage > 0 + return + } + + renderNext() { + const {currentPage, numPages} = this.props + const canPage = (currentPage + 1) !== numPages + return + } + + calcRange() { + const {currentPage, itemsNumber, itemsPerPage} = this.props + const fromVal = currentPage === -1 ? 0 : currentPage * itemsPerPage + 1 + const toValTmp = ((currentPage + 1) * itemsPerPage) + const toVal = toValTmp < itemsNumber ? toValTmp : itemsNumber + return `${fromVal} - ${toVal}` + } + + renderCounter() { + const {itemsNumber, locale} = this.props + return
+ {this.calcRange()} {tr.of[locale]} {itemsNumber} +
+ } + + render() { + + if (typeof this.props.currentPage === 'undefined') { + throw new TypeError('Must pass a currentPage argument to Paginator'); + } + return ( +
+ {this.renderCounter()} +
+ {this.renderPrevious()} + {this.renderNext()} +
+
+ ); + } +}; diff --git a/src/reactable/filterer.jsx b/src/reactable/filterer.jsx index a21cdb19..a8ccaf1f 100644 --- a/src/reactable/filterer.jsx +++ b/src/reactable/filterer.jsx @@ -1,19 +1,22 @@ import React from 'react'; -import ReactDOM from 'react-dom'; export class FiltererInput extends React.Component { - onChange() { - this.props.onFilter(ReactDOM.findDOMNode(this).value); - } - render() { + const {onFilter, onClean, filterCleanBtn, placeholder, value} = this.props return ( - +
+ onFilter(ev.target.value)} + /> + {filterCleanBtn && +
onClean()} className="reactable-btn-clean"> + × +
+ } +
); } }; @@ -27,12 +30,15 @@ export class Filterer extends React.Component { return ( ); } }; - diff --git a/src/reactable/lib/filter_props_from.jsx b/src/reactable/lib/filter_props_from.jsx index 88e9197a..8de81cb8 100644 --- a/src/reactable/lib/filter_props_from.jsx +++ b/src/reactable/lib/filter_props_from.jsx @@ -8,7 +8,23 @@ const internalProps = { itemsPerPage: true, childNode: true, data: true, - children: true + children: true, + topPagination: true, + itemsNumber: true, + numPages: true, + currentPage: true, + topPaginationElem: true, + filtering: true, + onFilter: true, + filterCleanBtn: true, + onClean: true, + onPageChange: true, + filterPlaceholder: true, + currentFilter: true, + sort: true, + sortableColumns: true, + onSort: true, + locale: true, }; export function filterPropsFrom(baseProps) { @@ -22,4 +38,3 @@ export function filterPropsFrom(baseProps) { return props; } - diff --git a/src/reactable/paginator.jsx b/src/reactable/paginator.jsx index 5586c20a..96c083f1 100644 --- a/src/reactable/paginator.jsx +++ b/src/reactable/paginator.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import { BtnPaginator } from './btnPaginator'; function pageHref(num) { return `#page-${num + 1}` @@ -21,27 +22,38 @@ export class Paginator extends React.Component { } renderPrevious() { + const previousBtn = { + nl: "Vorige", + fr: "Précédente", + en: "Previous", + } + const {locale} = this.props if(this.props.currentPage > 0) { return - Previous + {previousBtn[locale] || previousBtn.en} } } renderNext() { + const nextBtn = { + nl: "Volgende", + fr: "Suivante", + en: "Next", + } + const {locale} = this.props if(this.props.currentPage < this.props.numPages - 1) { return - Next + {nextBtn[locale] || nextBtn.en} } } renderPageButton(className, pageNum) { - return upperHalf) { pageButtons.splice(pageButtonLimit, pageButtons.length - pageButtonLimit); } - return ( - - - + {bottomPagination ? + + + : + + + + } ); } }; - diff --git a/src/reactable/table.jsx b/src/reactable/table.jsx index 58831116..89a74903 100644 --- a/src/reactable/table.jsx +++ b/src/reactable/table.jsx @@ -89,9 +89,6 @@ export class Table extends React.Component { } else if (typeof(descendant.props.children) !== 'undefined') { value = descendant.props.children; } else { - console.warn('exports.Td specified without ' + - 'a `data` property or children, ' + - 'ignoring'); return; } @@ -206,14 +203,48 @@ export class Table extends React.Component { componentWillMount() { this.initialize(this.props); this.sortByCurrentSort(); - this.filterBy(this.props.filterBy); + this.filterBy(this.props.filterBy != null ? this.props.filterBy : this.state.filter); } componentWillReceiveProps(nextProps) { this.initialize(nextProps); this.updateCurrentSort(nextProps.sortBy); this.sortByCurrentSort(); - this.filterBy(nextProps.filterBy); + this.filterBy(nextProps.filterBy != null ? nextProps.filterBy : this.state.filter); + } + + componentDidUpdate() { + this.diffChildren() + } + + componentDidMount() { + this.diffChildren() + } + + diffChildren() { + const {onVisibleChange} = this.props + const {lastChildren, lastIds, currentChildren} = this + if (!onVisibleChange || !currentChildren) {return} + + if (lastChildren === currentChildren) {return} + const currentIds = this.childrenToData(currentChildren, true) + + let same + if (lastIds && lastIds.length === currentIds.length) { + same = true + for (let i = 0; i < currentIds.length; i++) { + if (lastIds[i] !== currentIds[i]) { + same = false + break + } + } + } + + this.lastIds = currentIds + this.lastChildren = currentChildren + if (!same) { + onVisibleChange(currentIds) + } } applyFilter(filter, children) { @@ -306,6 +337,35 @@ export class Table extends React.Component { } } + childrenToData(children, onlyIds) { + return children.map(row => { + const {i, id} = row.props; + const iOrId = i != null ? i : id; + const idOrKey = iOrId != null ? iOrId : row.key; + + if (onlyIds) { + return idOrKey + } + + const data = {id: idOrKey}; + Object.keys(row.props.data).forEach(key => { + const col = row.props.data[key]; + data[key] = col.props.value != null ? col.props.value : col.value; + }); + + return data; + }) + } + visibleItems(onlyIds) { + return this.childrenToData(onlyIds) + } + + scrollToTop() { + if (this.headEl) { + this.headEl.scrollIntoView(); + } + } + render() { let children = []; let columns; @@ -386,15 +446,16 @@ export class Table extends React.Component { // Determine if we render the filter box let filtering = false; if ( - this.props.filterable && - Array.isArray(this.props.filterable) && - this.props.filterable.length > 0 && - !this.props.hideFilterInput + Array.isArray(this.props.filterable) && + this.props.filterable.length > 0 && + !this.props.hideFilterInput + ) { filtering = true; } // Apply filters + const filterCleanBtn = this.props.filterCleanBtn && this.state.filter; let filteredChildren = children; if (this.state.filter !== '') { filteredChildren = this.applyFilter(this.state.filter, filteredChildren); @@ -403,6 +464,8 @@ export class Table extends React.Component { // Determine pagination properties and which columns to display let itemsPerPage = 0; let pagination = false; + let topPagination = this.props.topPagination || false; + let bottomPagination = this.props.bottomPagination || false; let numPages; let currentPage = this.state.currentPage; let pageButtonLimit = this.props.pageButtonLimit || 10; @@ -416,7 +479,7 @@ export class Table extends React.Component { currentPage = numPages - 1; } - pagination = true; + pagination = numPages > 1; currentChildren = filteredChildren.slice( currentPage * itemsPerPage, (currentPage + 1) * itemsPerPage @@ -428,41 +491,69 @@ export class Table extends React.Component { let noDataText = this.props.noDataText ? : null; - return
s',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table'},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name'},Reactable.unsafe('Griffin Smith')),React.createElement(Reactable.Td,{column:'Age'},'18')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name'},Reactable.unsafe('Lee Salminen')),React.createElement(Reactable.Td,{column:'Age'},'23')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Position'},Reactable.unsafe('Developer')),React.createElement(Reactable.Td,{column:'Age'},'28'))),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the HTML in the table cells',function(){var griffins_name=$('span#griffins-name');expect(griffins_name.length).to.equal(1);expect(griffins_name).to.have.text('Griffin Smith');var lees_name=$('span#lees-name');expect(lees_name.length).to.equal(1);expect(lees_name).to.have.text('Lee Salminen');var who_knows_job=$('span#who-knows-job');expect(who_knows_job.length).to.equal(1);expect(who_knows_job).to.have.text('Developer');});});});describe('pagination',function(){describe('specifying pageButtonLimit',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Test Person'},{'Name':'Ian Zhang','Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18','Position':'Software Developer'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Test Person'},{'Name':'Ian Zhang','Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18','Position':'Software Developer'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Test Person'},{'Name':'Ian Zhang','Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18','Position':'Software Developer'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'}],itemsPerPage:2,pageButtonLimit:8}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('shows no more page buttons than the pageButtonLimit',function(){var pageButtons=$('#table tbody.reactable-pagination a.reactable-page-button');expect(pageButtons.length).to.equal(8);});});describe('specifying itemsPerPage',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Test Person'},{'Name':'Ian Zhang','Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18','Position':'Software Developer'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'}],itemsPerPage:4}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('provides buttons for each page',function(){var pageButtons=$('#table tbody.reactable-pagination a.reactable-page-button');expect(pageButtons.length).to.equal(3);expect($(pageButtons[0])).to.have.text('1');expect($(pageButtons[1])).to.have.text('2');expect($(pageButtons[2])).to.have.text('3');});it('displays only the first n rows',function(){expect($('#table tbody.reactable-data tr').length).to.equal(4);});it('specifies a class on the currently active page',function(){var activePage=$('#table tbody.reactable-pagination a.reactable-page-button.reactable-current-page');expect(activePage.length).to.equal(1);expect(activePage).to.have.text('1');});it('does not show previous button',function(){var previousButton=$('#table tbody.reactable-pagination a.reactable-previous-page');expect(previousButton.length).to.equal(0);});it('shows next button',function(){var nextButton=$('#table tbody.reactable-pagination a.reactable-next-page');expect(nextButton.length).to.equal(1);});describe('clicking page buttons',function(){beforeEach(function(){var page2=$('#table tbody.reactable-pagination a.reactable-page-button')[1];ReactTestUtils.Simulate.click(page2);});it('loads the next n rows',function(){var rows=$('#table tbody.reactable-data tr');expect($($(rows[0]).find('td')[0])).to.have.text('Test Person');expect($($(rows[1]).find('td')[0])).to.have.text('Ian Zhang');expect($($(rows[2]).find('td')[0])).to.have.text('Griffin Smith');expect($($(rows[3]).find('td')[0])).to.have.text('Lee Salminen');});it('puts an active class on the new active page',function(){var activePage=$('#table tbody.reactable-pagination a.reactable-page-button.reactable-current-page');expect(activePage.length).to.equal(1);expect(activePage).to.have.text('2');});it('can go back to the original page',function(){var page1=$('#table tbody.reactable-pagination a.reactable-page-button')[0];ReactTestUtils.Simulate.click(page1);var rows=$('#table tbody.reactable-data tr');expect($($(rows[0]).find('td')[0])).to.have.text('Griffin Smith');expect($($(rows[1]).find('td')[0])).to.have.text('Lee Salminen');expect($($(rows[2]).find('td')[0])).to.have.text('');expect($($(rows[3]).find('td')[0])).to.have.text('Griffin Smith');});it('shows previous button',function(){var previousButton=$('#table tbody.reactable-pagination a.reactable-previous-page');expect(previousButton.length).to.equal(1);});});});describe('specifying more itemsPerPage than items',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Test Person'},{'Name':'Ian Zhang','Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18','Position':'Software Developer'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'}],itemsPerPage:20}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders all rows',function(){expect($('#table tbody.reactable-data tr').length).to.equal(9);});it('provides buttons for 1 page',function(){var pageButtons=$('#table tbody.reactable-pagination a.reactable-page-button');expect(pageButtons.length).to.equal(1);expect($(pageButtons[0])).to.have.text('1');});it('does not show previous and next buttons',function(){var previousButton=$('#table tbody.reactable-pagination a.reactable-previous-page');var nextButton=$('#table tbody.reactable-pagination a.reactable-next-page');expect(previousButton.length + nextButton.length).to.equal(0);});});describe('not specifying itemsPerPage',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Test Person'},{'Name':'Ian Zhang','Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18','Position':'Software Developer'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'}]}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders all rows',function(){expect($('#table tbody.reactable-data tr').length).to.equal(9);});});describe('specifying 0 itemsPerPage',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Test Person'},{'Name':'Ian Zhang','Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18','Position':'Software Developer'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'}],itemsPerPage:0}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders all rows',function(){expect($('#table tbody.reactable-data tr').length).to.equal(9);});});describe('onPageChange hook',function(){var currentPage=undefined;var callback=function callback(page){currentPage = page;};before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18'},{'Age':'23','Name':'Test Person'},{'Name':'Ian Zhang','Age':'28','Position':'Developer'},{'Name':'Griffin Smith','Age':'18','Position':'Software Developer'},{'Age':'23','Name':'Lee Salminen'},{'Age':'28','Position':'Developer'}],itemsPerPage:4,onPageChange:callback}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('emits the number of the currently selected page (zero based) when onPageChange event is triggered',function(){var page1=$('#table tbody.reactable-pagination a.reactable-page-button')[0];var page2=$('#table tbody.reactable-pagination a.reactable-page-button')[1];var page3=$('#table tbody.reactable-pagination a.reactable-page-button')[2];ReactTestUtils.Simulate.click(page2);expect(currentPage).to.equal(1);ReactTestUtils.Simulate.click(page1);expect(currentPage).to.equal(0);ReactTestUtils.Simulate.click(page3);expect(currentPage).to.equal(2);});});});describe('sorting',function(){describe('no default sort',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{Name:'Lee Salminen',Age:'23',Position:'Programmer'},{Name:'Griffin Smith',Age:'18',Position:'Engineer'},{Name:'Ian Zhang',Age:'28',Position:'Developer'}],sortable:[{column:'Name',sortFunction:function sortFunction(a,b){ // Sort by last name +var nameA=a.split(' ');var nameB=b.split(' ');return nameA[1].localeCompare(nameB[1]);}},'Age','Position']}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders all rows with no sort',function(){ReactableTestUtils.expectRowText(0,['Lee Salminen','23','Programmer']);ReactableTestUtils.expectRowText(1,['Griffin Smith','18','Engineer']);ReactableTestUtils.expectRowText(2,['Ian Zhang','28','Developer']);});it('adds reactable-header-sortable to all headers',function(){var header=$('#table thead tr.reactable-column-header th')[0];expect($(header)).to.have['class']('reactable-header-sortable');header = $('#table thead tr.reactable-column-header th')[1];expect($(header)).to.have['class']('reactable-header-sortable');header = $('#table thead tr.reactable-column-header th')[2];expect($(header)).to.have['class']('reactable-header-sortable');});it('sorts by text in ascending order',function(){var positionHeader=$('#table thead tr.reactable-column-header th')[2];ReactTestUtils.Simulate.click(positionHeader);ReactableTestUtils.expectRowText(0,['Ian Zhang','28','Developer']);ReactableTestUtils.expectRowText(1,['Griffin Smith','18','Engineer']);ReactableTestUtils.expectRowText(2,['Lee Salminen','23','Programmer']); // Make sure the headers have the right classes +expect($(positionHeader)).to.have['class']('reactable-header-sort-asc');});it('sorts by text in descending order',function(){var positionHeader=$('#table thead tr.reactable-column-header th')[2];ReactTestUtils.Simulate.click(positionHeader);ReactableTestUtils.expectRowText(0,['Lee Salminen','23','Programmer']);ReactableTestUtils.expectRowText(1,['Griffin Smith','18','Engineer']);ReactableTestUtils.expectRowText(2,['Ian Zhang','28','Developer']); // Make sure the headers have the right classes +expect($(positionHeader)).to.have['class']('reactable-header-sort-desc');});it('sorts by last name in ascending order',function(){var nameHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.click(nameHeader);ReactableTestUtils.expectRowText(0,['Lee Salminen','23','Programmer']);ReactableTestUtils.expectRowText(1,['Griffin Smith','18','Engineer']);ReactableTestUtils.expectRowText(2,['Ian Zhang','28','Developer']); // Make sure the headers have the right classes +expect($(nameHeader)).to.have['class']('reactable-header-sort-asc');});it('sorts by last name in descending order',function(){var nameHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.click(nameHeader);ReactableTestUtils.expectRowText(0,['Ian Zhang','28','Developer']);ReactableTestUtils.expectRowText(1,['Griffin Smith','18','Engineer']);ReactableTestUtils.expectRowText(2,['Lee Salminen','23','Programmer']); // Make sure the headers have the right classes +expect($(nameHeader)).to.have['class']('reactable-header-sort-desc');});it('sorts by last name in ascending order by enter keydown',function(){var nameHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.keyDown(nameHeader,{keyCode:13});ReactableTestUtils.expectRowText(0,['Lee Salminen','23','Programmer']);ReactableTestUtils.expectRowText(1,['Griffin Smith','18','Engineer']);ReactableTestUtils.expectRowText(2,['Ian Zhang','28','Developer']); // Make sure the headers have the right classes +expect($(nameHeader)).to.have['class']('reactable-header-sort-asc');});it('does not sort on non-enter keydown',function(){var nameHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.keyDown(nameHeader,{keyCode:10});ReactableTestUtils.expectRowText(0,['Lee Salminen','23','Programmer']);ReactableTestUtils.expectRowText(1,['Griffin Smith','18','Engineer']);ReactableTestUtils.expectRowText(2,['Ian Zhang','28','Developer']); // Make sure the headers have the right classes +expect($(nameHeader)).to.have['class']('reactable-header-sort-asc');});});describe('passing `true` to sortable',function(){var component;before(function(){this.render = function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{Name:'Lee Salminen',Age:'23',Position:'Programmer'},{Name:'Griffin Smith',Age:'18',Position:'Engineer'},{Name:'Ian Zhang',Age:'28',Position:'Developer'}],sortable:true}),ReactableTestUtils.testNode());};this.render();});after(ReactableTestUtils.resetTestEnvironment);it('sorts by the first column in ascending order',function(){var nameHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.click(nameHeader);ReactableTestUtils.expectRowText(0,['Griffin Smith','18','Engineer']);ReactableTestUtils.expectRowText(1,['Ian Zhang','28','Developer']);ReactableTestUtils.expectRowText(2,['Lee Salminen','23','Programmer']); // Make sure the headers have the right classes +expect($(nameHeader)).to.have['class']('reactable-header-sort-asc');});it('sorts by the first column in descending order',function(){var nameHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.click(nameHeader);ReactableTestUtils.expectRowText(0,['Lee Salminen','23','Programmer']);ReactableTestUtils.expectRowText(1,['Ian Zhang','28','Developer']);ReactableTestUtils.expectRowText(2,['Griffin Smith','18','Engineer']); // Make sure the headers have the right classes +expect($(nameHeader)).to.have['class']('reactable-header-sort-desc');});it('sorts by the second column in ascending order',function(){var nameHeader=$('#table thead tr.reactable-column-header th')[1];ReactTestUtils.Simulate.click(nameHeader);ReactableTestUtils.expectRowText(0,['Griffin Smith','18','Engineer']);ReactableTestUtils.expectRowText(1,['Lee Salminen','23','Programmer']);ReactableTestUtils.expectRowText(2,['Ian Zhang','28','Developer']); // Make sure the headers have the right classes +expect($(nameHeader)).to.have['class']('reactable-header-sort-asc');});it('sorts by the second column in descending order',function(){var nameHeader=$('#table thead tr.reactable-column-header th')[1];ReactTestUtils.Simulate.click(nameHeader);ReactableTestUtils.expectRowText(0,['Ian Zhang','28','Developer']);ReactableTestUtils.expectRowText(1,['Lee Salminen','23','Programmer']);ReactableTestUtils.expectRowText(2,['Griffin Smith','18','Engineer']); // Make sure the headers have the right classes +expect($(nameHeader)).to.have['class']('reactable-header-sort-desc');});it('sorts by the third column in ascending order',function(){var positionHeader=$('#table thead tr.reactable-column-header th')[2];ReactTestUtils.Simulate.click(positionHeader);ReactableTestUtils.expectRowText(0,['Ian Zhang','28','Developer']);ReactableTestUtils.expectRowText(1,['Griffin Smith','18','Engineer']);ReactableTestUtils.expectRowText(2,['Lee Salminen','23','Programmer']); // Make sure the headers have the right classes +expect($(positionHeader)).to.have['class']('reactable-header-sort-asc');});it('sorts by the third column in descending order',function(){var positionHeader=$('#table thead tr.reactable-column-header th')[2];ReactTestUtils.Simulate.click(positionHeader);ReactableTestUtils.expectRowText(0,['Lee Salminen','23','Programmer']);ReactableTestUtils.expectRowText(1,['Griffin Smith','18','Engineer']);ReactableTestUtils.expectRowText(2,['Ian Zhang','28','Developer']); // Make sure the headers have the right classes +expect($(positionHeader)).to.have['class']('reactable-header-sort-desc');});it('Keeps the same sort after rerendering',function(){expect(this.render).to.not['throw'](Error);ReactableTestUtils.expectRowText(0,['Lee Salminen','23','Programmer']);ReactableTestUtils.expectRowText(1,['Griffin Smith','18','Engineer']);ReactableTestUtils.expectRowText(2,['Ian Zhang','28','Developer']);});});describe('default sort',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{Name:'Lee Salminen',Age:'23',Position:'Programmer'},{Name:'Griffin Smith',Age:'18',Position:'Engineer'},{Name:'Ian Zhang',Age:'28',Position:'Developer'}],sortable:[{column:'Name',sortFunction:function sortFunction(a,b){ // Sort by last name +var nameA=a.split(' ');var nameB=b.split(' ');return nameA[1].localeCompare(nameB[1]);}},'Age','Position'],defaultSort:{column:'Age',direction:'desc'}}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders all rows sorted by default column age descending',function(){ReactableTestUtils.expectRowText(0,['Ian Zhang','28','Developer']);ReactableTestUtils.expectRowText(1,['Lee Salminen','23','Programmer']);ReactableTestUtils.expectRowText(2,['Griffin Smith','18','Engineer']);});});describe('sort descending by default flag',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{Name:'Lee Salminen',Age:'23',Position:'Programmer'},{Name:'Griffin Smith',Age:'18',Position:'Engineer'},{Name:'Ian Zhang',Age:'28',Position:'Developer'}],sortable:[{column:'Name',sortFunction:function sortFunction(a,b){ // Sort by last name +var nameA=a.split(' ');var nameB=b.split(' ');return nameA[1].localeCompare(nameB[1]);}},'Age','Position'],defaultSort:{column:'Age'},defaultSortDescending:true}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders all rows sorted by default column age descending',function(){ReactableTestUtils.expectRowText(0,['Ian Zhang','28','Developer']);ReactableTestUtils.expectRowText(1,['Lee Salminen','23','Programmer']);ReactableTestUtils.expectRowText(2,['Griffin Smith','18','Engineer']);});it('sorts by the age column in ascending order',function(){var positionHeader=$('#table thead tr.reactable-column-header th')[1];ReactTestUtils.Simulate.click(positionHeader);ReactableTestUtils.expectRowText(0,['Griffin Smith','18','Engineer']);ReactableTestUtils.expectRowText(1,['Lee Salminen','23','Programmer']);ReactableTestUtils.expectRowText(2,['Ian Zhang','28','Developer']); // Make sure the headers have the right classes +expect($(positionHeader)).to.have['class']('reactable-header-sort-asc');});});describe('default sort no direction specified',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{Name:'Lee Salminen',Age:'23',Position:'Programmer'},{Name:'Griffin Smith',Age:'18',Position:'Engineer'},{Name:'Ian Zhang',Age:'28',Position:'Developer'}],sortable:[{column:'Name',sortFunction:function sortFunction(a,b){ // Sort by last name +var nameA=a.split(' ');var nameB=b.split(' ');return nameA[1].localeCompare(nameB[1]);}},'Age','Position'],defaultSort:'Age'}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders all rows sorted by default column age ascending',function(){ReactableTestUtils.expectRowText(0,['Griffin Smith','18','Engineer']);ReactableTestUtils.expectRowText(1,['Lee Salminen','23','Programmer']);ReactableTestUtils.expectRowText(2,['Ian Zhang','28','Developer']);});});describe('unsortable column',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{Name:'Lee Salminen',Age:'23',Position:'Programmer'},{Name:'Griffin Smith',Age:'18',Position:'Engineer'},{Name:'Ian Zhang',Age:'28',Position:'Developer'}],sortable:['Age','Position']}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('leaves columns unsorted',function(){var nameHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.click(nameHeader);ReactableTestUtils.expectRowText(0,['Lee Salminen','23','Programmer']);ReactableTestUtils.expectRowText(1,['Griffin Smith','18','Engineer']);ReactableTestUtils.expectRowText(2,['Ian Zhang','28','Developer']);});});[Reactable.Sort.Numeric,Reactable.Sort.NumericInteger].forEach(function(method){describe('numeric sort',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{Count:'23'},{Count:'18'},{Count:'28'},{Count:'1.23'},{Count:'a'},{Count:'z'},{Count:'123'}],columns:[{key:'Count',sortable:method}]}),ReactableTestUtils.testNode());});after(function(){ReactableTestUtils.resetTestEnvironment();});it('sorts columns numerically',function(){var sortHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.click(sortHeader);ReactableTestUtils.expectRowText(0,['1.23']);ReactableTestUtils.expectRowText(1,['18']);ReactableTestUtils.expectRowText(2,['23']);ReactableTestUtils.expectRowText(3,['28']);ReactableTestUtils.expectRowText(4,['123']);ReactableTestUtils.expectRowText(5,['a']);ReactableTestUtils.expectRowText(6,['z']);});});});describe('numeric sort with Tr and Td specified',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',columns:[{key:'Count',sortable:Reactable.Sort.Numeric}]},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Count'},'23')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Count'},'18')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Count'},'28')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Count'},'1.23')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Count'},'a')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Count'},'z')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Count'},'123'))),ReactableTestUtils.testNode());});after(function(){ReactableTestUtils.resetTestEnvironment();});it('sorts columns numerically',function(){var sortHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.click(sortHeader);ReactableTestUtils.expectRowText(0,['1.23']);ReactableTestUtils.expectRowText(1,['18']);ReactableTestUtils.expectRowText(2,['23']);ReactableTestUtils.expectRowText(3,['28']);ReactableTestUtils.expectRowText(4,['123']);ReactableTestUtils.expectRowText(5,['a']);ReactableTestUtils.expectRowText(6,['z']);});});describe('numeric sort with Tr and Td specified and custom value',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',columns:[{key:'Count',sortable:Reactable.Sort.Numeric}]},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Count',value:23},'twenty-three')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Count',value:18},'eighteen')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Count',value:28},'twenty-eight')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Count',value:1.23},'one point two three')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Count',value:'a'},'a')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Count',value:'z'},'z')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Count',value:123},'one hundred twenty-three'))),ReactableTestUtils.testNode());});after(function(){ReactableTestUtils.resetTestEnvironment();});it('sorts columns numerically',function(){var sortHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.click(sortHeader);ReactableTestUtils.expectRowText(0,['one point two three']);ReactableTestUtils.expectRowText(1,['eighteen']);ReactableTestUtils.expectRowText(2,['twenty-three']);ReactableTestUtils.expectRowText(3,['twenty-eight']);ReactableTestUtils.expectRowText(4,['one hundred twenty-three']);ReactableTestUtils.expectRowText(5,['a']);ReactableTestUtils.expectRowText(6,['z']);});});describe('currency sort',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{Price:'1.25'},{Price:'$1.01'},{Price:'1'},{Price:'$10,000'},{Price:'$10,500'},{Price:'$10'},{Price:'a'},{Price:'z'},{Price:'$2'},{Price:'$.5'},{Price:'$0.60'},{Price:'.1'}],columns:[{key:'Price',sortable:Reactable.Sort.Currency}]}),ReactableTestUtils.testNode());});after(function(){ReactableTestUtils.resetTestEnvironment();});it('sorts columns numerically. parsing out currency symbols',function(){var sortHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.click(sortHeader);ReactableTestUtils.expectRowText(0,['.1']);ReactableTestUtils.expectRowText(1,['$.5']);ReactableTestUtils.expectRowText(2,['$0.60']);ReactableTestUtils.expectRowText(3,['1']);ReactableTestUtils.expectRowText(4,['$1.01']);ReactableTestUtils.expectRowText(5,['1.25']);ReactableTestUtils.expectRowText(6,['$2']);ReactableTestUtils.expectRowText(7,['$10']);ReactableTestUtils.expectRowText(8,['$10,000']);ReactableTestUtils.expectRowText(9,['$10,500']);ReactableTestUtils.expectRowText(10,['a']);ReactableTestUtils.expectRowText(11,['z']);});});describe('date sort',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{'Date':'1/1/2014 11:00 AM'},{'Date':'1/1/2013 11:00 AM'},{'Date':'1/1/2014 4:30 PM'},{'Date':'4/3/2013'},{'Date':'a'},{'Date':'z'}],columns:[{key:'Date',sortable:Reactable.Sort.Date}]}),ReactableTestUtils.testNode());});after(function(){ReactableTestUtils.resetTestEnvironment();});it('sorts columns by date',function(){var sortHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.click(sortHeader);ReactableTestUtils.expectRowText(0,['1/1/2013 11:00 AM']);ReactableTestUtils.expectRowText(1,['4/3/2013']);ReactableTestUtils.expectRowText(2,['1/1/2014 11:00 AM']);ReactableTestUtils.expectRowText(3,['1/1/2014 4:30 PM']);ReactableTestUtils.expectRowText(4,['a']);ReactableTestUtils.expectRowText(5,['z']);});});describe('case insensitive sorting',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{'Name':'Lee Salminen'},{'Name':'Griffin Smith'},{'Name':'Ian Zhang'},{'Name':'lee Salminen'},{'Name':'griffin smith'},{'Name':'Ian zhang'}],columns:[{key:'Name',sortable:Reactable.Sort.CaseInsensitive}]}),ReactableTestUtils.testNode());});after(function(){ReactableTestUtils.resetTestEnvironment();});it('sorts columns by value - case insensitive',function(){var sortHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.click(sortHeader);ReactableTestUtils.expectRowText(0,['Griffin Smith']);ReactableTestUtils.expectRowText(1,['griffin smith']);ReactableTestUtils.expectRowText(2,['Ian Zhang']);ReactableTestUtils.expectRowText(3,['Ian zhang']);ReactableTestUtils.expectRowText(4,['Lee Salminen']);ReactableTestUtils.expectRowText(5,['lee Salminen']);});});describe('custom sort with React Components',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{'Rank':React.createElement('span',{className:'3'},'Third')},{'Rank':React.createElement('span',{className:'1'},'First')},{'Rank':React.createElement('span',{className:'2'},'Second')}],columns:[{key:'Rank',sortable:function sortable(a,b){ // sort based on classname +return a.props.className.localeCompare(b.props.className);}}]}),ReactableTestUtils.testNode());});after(function(){ReactableTestUtils.resetTestEnvironment();});it('sorts columns by value',function(){var sortHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.click(sortHeader);ReactableTestUtils.expectRowText(0,['First']);ReactableTestUtils.expectRowText(1,['Second']);ReactableTestUtils.expectRowText(2,['Third']);});});describe('sorts and calls onSort callback via props',function(){var sortColumn=null;var callback=function callback(sortObject){sortColumn = sortObject.column;};before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{'Rank':React.createElement('span',{className:'3'},'Third')},{'Rank':React.createElement('span',{className:'1'},'First')},{'Rank':React.createElement('span',{className:'2'},'Second')}],columns:[{key:'Rank',sortable:function sortable(a,b){ // sort based on classname +return a.props.className.localeCompare(b.props.className);}}],onSort:callback}),ReactableTestUtils.testNode());});after(function(){ReactableTestUtils.resetTestEnvironment();});it('returns currentSort object to callback for utilization',function(){var sortHeader=$('#table thead tr.reactable-column-header th')[0];ReactTestUtils.Simulate.click(sortHeader);expect(sortColumn).to.equal('Rank');});});});describe('filtering',function(){describe('filtering with javascript objects for data',function(){var data=[{name:"Lee SomeoneElse",age:18},{name:"Lee Salminen",age:23},{name:"No Age",age:null}];var filterBy;var onFilter=function onFilter(filter){filterBy = filter;};before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',filterable:['Name','Age'],onFilter:onFilter},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name',data:data[0].name}),React.createElement(Reactable.Td,{column:'Age',data:data[0].age})),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name',data:data[1].name}),React.createElement(Reactable.Td,{column:'Age',data:data[1].age})),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name',data:data[2].name}),React.createElement(Reactable.Td,{column:'Age',data:data[2].age}))),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('filters case insensitive on specified columns',function(){var $filter=$('#table thead tr.reactable-filterer input.reactable-filter-input');$filter.val('lee');React.addons.TestUtils.Simulate.keyUp($filter[0]);ReactableTestUtils.expectRowText(0,['Lee SomeoneElse','18']);ReactableTestUtils.expectRowText(1,['Lee Salminen','23']);});it('calls onFilter event handler',function(){var $filter=$('#table thead tr.reactable-filterer input.reactable-filter-input');var textToSearch='lee';$filter.val(textToSearch);React.addons.TestUtils.Simulate.keyUp($filter[0]);expect(filterBy).to.equal(textToSearch);});});describe('basic case-insensitive filtering',function(){before(function(){this.component = ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',filterable:['State','Tag'],filterPlaceholder:'Filter Results',columns:['State','Description','Tag']},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'State'},'New York'),React.createElement(Reactable.Td,{column:'Description'},'this is some text'),React.createElement(Reactable.Td,{column:'Tag'},'new')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'State'},'New Mexico'),React.createElement(Reactable.Td,{column:'Description'},'lorem ipsum'),React.createElement(Reactable.Td,{column:'Tag'},'old')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'State'},'Colorado'),React.createElement(Reactable.Td,{column:'Description'},'new description that shouldnt match filter'),React.createElement(Reactable.Td,{column:'Tag'},'old')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'State'},'Alaska'),React.createElement(Reactable.Td,{column:'Description'},'bacon'),React.createElement(Reactable.Td,{column:'Tag'},'renewed'))),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);context('from the filterer field',function(){it('filters case insensitive on specified columns',function(){var $filter=$('#table thead tr.reactable-filterer input.reactable-filter-input');$filter.val('new');React.addons.TestUtils.Simulate.keyUp($filter[0]);ReactableTestUtils.expectRowText(0,['New York','this is some text','new']);ReactableTestUtils.expectRowText(1,['New Mexico','lorem ipsum','old']);ReactableTestUtils.expectRowText(2,['Alaska','bacon','renewed']);});it('filter placeholder is set',function(){var $filter=$('#table thead tr.reactable-filterer input.reactable-filter-input');expect($filter.attr("placeholder")).to.equal('Filter Results');});});context('from the function',function(){before(function(){this.component.filterBy('york');});it('applies the filtering',function(){ReactableTestUtils.expectRowText(0,['New York','this is some text','new']);});it('updates the value of the filterer',function(){var $filter=$('#table thead tr.reactable-filterer input.reactable-filter-input');expect($filter).to.have.value('york');});});context('from filterBy prop',function(){before(function(){ReactableTestUtils.resetTestEnvironment();var ParentComponent=React.createClass({displayName:'ParentComponent',getInitialState:function getInitialState(){return {customFilterText:'new'};},handleChange:function handleChange(event){this.setState({customFilterText:event.target.value});},render:function render(){return React.createElement('div',null,React.createElement('input',{type:'text',ref:'customFilterInput',id:'customFilterInput',value:this.state.customFilterText,onChange:this.handleChange}),React.createElement(Reactable.Table,{className:'table',id:'table',filterable:['State','Tag'],filterPlaceholder:'Filter Results',filterBy:this.state.customFilterText,columns:['State','Description','Tag']},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'State'},'New York'),React.createElement(Reactable.Td,{column:'Description'},'this is some text'),React.createElement(Reactable.Td,{column:'Tag'},'new')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'State'},'New Mexico'),React.createElement(Reactable.Td,{column:'Description'},'lorem ipsum'),React.createElement(Reactable.Td,{column:'Tag'},'old')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'State'},'Colorado'),React.createElement(Reactable.Td,{column:'Description'},'new description that shouldnt match filter'),React.createElement(Reactable.Td,{column:'Tag'},'old')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'State'},'Alaska'),React.createElement(Reactable.Td,{column:'Description'},'bacon'),React.createElement(Reactable.Td,{column:'Tag'},'renewed'))));}});this.component = ReactDOM.render(React.createElement(ParentComponent),ReactableTestUtils.testNode());});it('filters case insensitive on specified columns',function(){ReactableTestUtils.expectRowText(0,['New York','this is some text','new']);ReactableTestUtils.expectRowText(1,['New Mexico','lorem ipsum','old']);ReactableTestUtils.expectRowText(2,['Alaska','bacon','renewed']);var $builtInFilter=$('#table thead tr.reactable-filterer input.reactable-filter-input');expect($builtInFilter).to.have.value('new'); // Simulate changing input on parent component and re-rendering Reactable.Table with new props. +var node=this.component.refs.customFilterInput;node.value = 'alaska';ReactTestUtils.Simulate.change(customFilterInput);ReactableTestUtils.expectRowText(0,['Alaska','bacon','renewed']);expect($builtInFilter).to.have.value('alaska');});});});context('filtering with prop and hiding filter input',function(){before(function(){this.component = ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',filterable:['State','Tag'],filterPlaceholder:'Filter Results',filterBy:'new',hideFilterInput:true,columns:['State','Description','Tag']},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'State'},'New York'),React.createElement(Reactable.Td,{column:'Description'},'this is some text'),React.createElement(Reactable.Td,{column:'Tag'},'new')),React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'State'},'New Mexico'),React.createElement(Reactable.Td,{column:'Description'},'lorem ipsum'),React.createElement(Reactable.Td,{column:'Tag'},'old'))),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('does not render the filter input box',function(){expect($('#table thead tr.reactable-filterer input.reactable-filter-input').length).to.equal(0);});});context('filtering and pagination together',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{'State':'New York','Description':'this is some text','Tag':'new'},{'State':'New Mexico','Description':'lorem ipsum','Tag':'old'},{'State':'Colorado','Description':'new description that shouldn\'t match filter','Tag':'old'},{'State':'Alaska','Description':'bacon','Tag':'renewed'}],filterable:['State','Tag'],columns:['State','Description','Tag'],itemsPerPage:2}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);afterEach(function(){var $filter=$('#table thead tr.reactable-filterer input.reactable-filter-input');$filter.val('');React.addons.TestUtils.Simulate.keyUp($filter[0]);});it('updates the pagination links',function(){var $filter=$('#table thead tr.reactable-filterer input.reactable-filter-input');$filter.val('colorado');React.addons.TestUtils.Simulate.keyUp($filter[0]);var pageButtons=$('#table tbody.reactable-pagination a.reactable-page-button');expect(pageButtons.length).to.equal(1);expect($(pageButtons[0])).to.have.text('1');});it('updates the current page if necessary',function(){var $filter=$('#table thead tr.reactable-filterer input.reactable-filter-input');var $pageButtons=$('#table tbody.reactable-pagination a.reactable-page-button'); // Go to the last page +React.addons.TestUtils.Simulate.click($pageButtons[1]); // Then filter so that that page doesn't exist anymore +$filter.val('colorado');React.addons.TestUtils.Simulate.keyUp($filter[0]);ReactableTestUtils.expectRowText(0,['Colorado',"new description that shouldn't match filter",'old']);var activePage=$('#table tbody.reactable-pagination ' + 'a.reactable-page-button.reactable-current-page');expect(activePage.length).to.equal(1);expect(activePage).to.have.text('1');});});});describe('directly passing a data array with non-string data',function(){before(function(){ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table',data:[{Name:'Griffin Smith',Age:18},{Age:23,Name:{toString:function toString(){return 'Lee Salminen';}}},{Age:28.45,Position:'Developer'}]}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('renders the table',function(){expect($('table#table.table')).to.exist;});it('renders the column headers in the table',function(){var headers=[];$('thead th').each(function(){headers.push($(this).text());});expect(headers).to.eql(['Name','Age','Position']);});it('renders the first row with the correct data',function(){ReactableTestUtils.expectRowText(0,['Griffin Smith','18','']);});it('renders the second row with the correct data',function(){ReactableTestUtils.expectRowText(1,['Lee Salminen','23','']);});it('renders the third row with the correct data',function(){ReactableTestUtils.expectRowText(2,['','28.45','Developer']);});});describe('multiple tables on a page',function(){before(function(){this.parentTestNode = ReactableTestUtils.testNode();this.testNode1 = $('
').attr('id','test-node-1');this.testNode2 = $('
').attr('id','test-node-2');ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table1',data:[{Name:'Griffin Smith',Age:'18'},{Age:'23',Name:'Lee Salminen'},{Age:'28',Position:'Developer'}]}),this.testNode1[0]);ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table2',data:[{Moniker:'Griffin Smith',Elderliness:'18'},{Elderliness:'23',Moniker:'Lee Salminen'},{Elderliness:'28',Title:'Developer'}]}),this.testNode2[0]);});after(function(){$(this.parentTestNode).empty().remove();});it('renders the column headers in the first table',function(){var headers=[];this.testNode1.find('thead th').each(function(){headers.push($(this).text());});expect(headers).to.eql(['Name','Age','Position']);});it('renders the column headers in the second table',function(){var headers=[];this.testNode2.find('thead th').each(function(){headers.push($(this).text());});expect(headers).to.eql(['Moniker','Elderliness','Title']);});});describe('handleClick callbacks',function(){before(function(){this.clicked = false;ReactDOM.render(React.createElement(Reactable.Table,{className:'table',id:'table'},React.createElement(Reactable.Tr,null,React.createElement(Reactable.Td,{column:'Name',handleClick:(function(){this.clicked = true;}).bind(this)},React.createElement('b',null,'Griffin Smith')))),ReactableTestUtils.testNode());ReactTestUtils.Simulate.click($('td')[0]);});after(ReactableTestUtils.resetTestEnvironment);it('calls the callbacks on click',function(){expect(this.clicked).to.eq(true);});});describe('table with no data',function(){context('when noDataText prop is null',function(){before(function(){this.component = ReactDOM.render(React.createElement(Reactable.Table,{data:[],columns:['State','Description','Tag']}),ReactableTestUtils.testNode());});after(ReactableTestUtils.resetTestEnvironment);it('does not render the reactable-no-data element',function(){expect($('.reactable-no-data').length).to.eq(0);});});context('when initialized without
- + placeholder={this.props.placeholder} + />
- {this.renderPrevious()} - {pageButtons} - {this.renderNext()} -
+
+
+ {bottomPaginationElem.left} +
+
+ +
+
+ {bottomPaginationElem.right} +
+
+
+ {this.renderPrevious()} + {pageButtons} + {this.renderNext()} +
{this.props.noDataText}
+ this.currentChildren = currentChildren; + return
{columns && columns.length > 0 ? - { - this.setState({ filter: filter }); - if (this.props.onFilter) { - this.props.onFilter(filter) - } - }} - filterPlaceholder={this.props.filterPlaceholder} - currentFilter={this.state.filter} - sort={this.state.currentSort} - sortableColumns={this._sortable} - onSort={this.onSort.bind(this)} - key="thead"/> - : null} + this.headEl = t} + columns={columns} + topPagination={topPagination} + itemsNumber={filteredChildren.length} + itemsPerPage={itemsPerPage} + numPages={numPages} + currentPage={currentPage} + topPaginationElem={{ + left: this.props.topPaginationElemL, + right: this.props.topPaginationElemR, + }} + filtering={filtering} + onFilter={filter => { + this.setState({ filter: filter }); + }} + filterCleanBtn={filterCleanBtn} + onClean={filter => { + this.setState({ filter: '' }); + }} + onPageChange={page => { + const {onPageChange} = this.props; + onPageChange && onPageChange(page); + this.setState({ currentPage: page }); + this.scrollToTop(); + }} + filterPlaceholder={this.props.filterPlaceholder} + currentFilter={this.state.filter} + sort={this.state.currentSort} + sortableColumns={this._sortable} + onSort={this.onSort.bind(this)} + key="thead" + locale={this.props.locale} + /> : null + } {currentChildren.length > 0 ? currentChildren : noDataText} - {pagination === true ? - { - this.setState({ currentPage: page }); - if (this.props.onPageChange) { - this.props.onPageChange(page) - } - }} - key="paginator"/> - : null} + {pagination ? + { + this.setState({ currentPage: page }); + this.scrollToTop() + }} + key="paginator" + /> : null + } {this.tfoot} -
; +
} } @@ -471,6 +562,6 @@ Table.defaultProps = { defaultSort: false, defaultSortDescending: false, itemsPerPage: 0, - filterBy: '', - hideFilterInput: false + hideFilterInput: false, + locale: 'en' }; diff --git a/src/reactable/td.jsx b/src/reactable/td.jsx index c695c821..eeb31891 100644 --- a/src/reactable/td.jsx +++ b/src/reactable/td.jsx @@ -22,10 +22,8 @@ export class Td extends React.Component { // Attach any properties on the column to this Td object to allow things like custom event handlers if (typeof(this.props.column) === 'object') { - for (var key in this.props.column) { - if (key !== 'key' && key !== 'name') { - tdProps[key] = this.props.column[key]; - } + for (var key in this.props.column.props) { + tdProps[key] = this.props.column[key]; } } diff --git a/src/reactable/thead.jsx b/src/reactable/thead.jsx index 9546f710..6a04b344 100644 --- a/src/reactable/thead.jsx +++ b/src/reactable/thead.jsx @@ -3,6 +3,9 @@ import { Th } from './th'; import { Filterer } from './filterer'; import { filterPropsFrom } from './lib/filter_props_from'; +import { BtnPaginator } from './btnPaginator'; +import { FiltererInput } from './filterer'; + export class Thead extends React.Component { static getColumns(component) { // Can't use React.Children.map since that doesn't return a proper array @@ -12,20 +15,21 @@ export class Thead extends React.Component { if (typeof th.props !== 'undefined') { column.props = filterPropsFrom(th.props); - // use the content as the label & key - if (typeof th.props.children !== 'undefined') { - column.label = th.props.children; - column.key = column.label; + //set the label depending on the props + if (typeof th.props.label !== 'undefined'){ + column.label = th.props.label + } else if (typeof th.props.children !== 'undefined'){ + column.label = th.props.children + } else if (typeof th.props.column === 'string') { + column.label = th.props.column; } // the key in the column attribute supersedes the one defined previously if (typeof th.props.column === 'string') { column.key = th.props.column; - - // in case we don't have a label yet - if (typeof column.label === 'undefined') { - column.label = column.key; - } + } else { + // use the content as the key + column.key = th.props.children; } } @@ -50,22 +54,30 @@ export class Thead extends React.Component { this.props.onSort(column.key); } } + + scrollIntoView() { + if (this.domEl && this.domEl.offsetParent === null) { + this.domEl.scrollIntoView(); + } + } render() { + const {sort, sortableColumns, filtering, columns, onFilter, onClean, filterPlaceholder, currentFilter, topPagination, filterCleanBtn, locale, itemsPerPage, itemsNumber, numPages, currentPage, onPageChange, topPaginationElem} = this.props; + // Declare the list of Ths var Ths = []; - for (var index = 0; index < this.props.columns.length; index++) { - var column = this.props.columns[index]; + for (var index = 0; index < columns.length; index++) { + var column = columns[index]; var thClass = `reactable-th-${column.key.replace(/\s+/g, '-').toLowerCase()}`; var sortClass = ''; - if (this.props.sortableColumns[column.key]) { + if (sortableColumns[column.key]) { sortClass += 'reactable-header-sortable '; } - if (this.props.sort.column === column.key) { + if (sort.column === column.key) { sortClass += 'reactable-header-sort'; - if (this.props.sort.direction === 1) { + if (sort.direction === 1) { sortClass += '-asc'; } else { @@ -99,16 +111,50 @@ export class Thead extends React.Component { // Manually transfer props var props = filterPropsFrom(this.props); - return ( -
+
+
+ {topPaginationElem.left} +
+
+ {filtering && + + } + +
+
+ {topPaginationElem.right} +
+
+
+ + + + + {data.map((item, i) => + + + )} + + + + +
The NameThe Age
+ +
+ +
+ +
+ ) + } + } + const doTest = (runMe, options = {}) => ( + ReactDOM.render( + , + ReactableTestUtils.testNode() + ) + ) + + after(ReactableTestUtils.resetTestEnvironment); + + it('works', () => { + doTest(me => { + const items = me.refs.table.visibleItems() + expect(items.length).to.eql(5) + expect(items[0].Age).to.eql(18) + }) + }); + it('works when filtered', () => { + doTest(me => { + const items = me.refs.table.visibleItems() + expect(items.length).to.eql(2) + expect(items[1].Age).to.eql(23) + }, {filterBy: 'lee'}) + }); + it('works when paged', () => { + doTest(me => { + const items = me.refs.table.visibleItems() + expect(items.length).to.eql(1) + expect(items[0].Age).to.eql(18) + }, {itemsPerPage: 1}) + }); + it('has keys', () => { + doTest(me => { + const items = me.refs.table.visibleItems() + expect(items[1].id).to.eql(1) + // id is not defined and key is a string + expect(items[2].id).to.eql("2") + // Falsy ids still count + expect(items[0].id).to.eql(0) + expect(items[3].id).to.eql(true) + expect(items[4].id).to.eql("id") + }) + }); + }); + + });