Allowed ability to set default sort_by/order on listings

- improved performance of listings (less refresh of items)
- fixed sorting issue where the order would not be reversed
This commit is contained in:
Jonathan Labreuille
2016-06-17 17:26:17 +02:00
parent 4bb1acf493
commit 7af2775972
4 changed files with 879 additions and 895 deletions

View File

@@ -1,21 +1,15 @@
define([ import MailPoet from 'mailpoet'
'react', import React from 'react'
'classnames', import classNames from 'classnames'
'mailpoet'
], function(
React,
classNames,
MailPoet
) {
var ListingHeader = React.createClass({ const ListingHeader = React.createClass({
handleSelectItems: function() { handleSelectItems: function() {
return this.props.onSelectItems( return this.props.onSelectItems(
this.refs.toggle.checked this.refs.toggle.checked
); );
}, },
render: function() { render: function() {
var columns = this.props.columns.map(function(column, index) { const columns = this.props.columns.map(function(column, index) {
column.is_primary = (index === 0); column.is_primary = (index === 0);
column.sorted = (this.props.sort_by === column.name) column.sorted = (this.props.sort_by === column.name)
? this.props.sort_order ? this.props.sort_order
@@ -29,7 +23,7 @@ define([
); );
}.bind(this)); }.bind(this));
var checkbox = false; let checkbox;
if(this.props.is_selectable === true) { if(this.props.is_selectable === true) {
checkbox = ( checkbox = (
@@ -57,21 +51,21 @@ define([
} }
}); });
var ListingColumn = React.createClass({ const ListingColumn = React.createClass({
handleSort: function() { handleSort: function() {
var sort_by = this.props.column.name, const sort_by = this.props.column.name;
sort_order = (this.props.column.sorted === 'asc') ? 'desc' : 'asc'; const sort_order = (this.props.column.sorted === 'asc') ? 'desc' : 'asc';
this.props.onSort(sort_by, sort_order); this.props.onSort(sort_by, sort_order);
}, },
render: function() { render: function() {
var classes = classNames( const classes = classNames(
'manage-column', 'manage-column',
{ 'column-primary': this.props.column.is_primary }, { 'column-primary': this.props.column.is_primary },
{ 'sortable': this.props.column.sortable }, { 'sortable': this.props.column.sortable },
this.props.column.sorted, this.props.column.sorted,
{ 'sorted': (this.props.sort_by === this.props.column.name) } { 'sorted': (this.props.sort_by === this.props.column.name) }
); );
var label; let label;
if(this.props.column.sortable === true) { if(this.props.column.sortable === true) {
label = ( label = (
@@ -94,5 +88,4 @@ define([
} }
}); });
return ListingHeader; module.exports = ListingHeader;
});

View File

@@ -1,33 +1,16 @@
define( import MailPoet from 'mailpoet'
[ import jQuery from 'jquery'
'mailpoet', import React from 'react'
'jquery', import { Router, Link } from 'react-router'
'react', import classNames from 'classnames'
'react-router', import ListingBulkActions from 'listing/bulk_actions.jsx'
'classnames', import ListingHeader from 'listing/header.jsx'
'listing/bulk_actions.jsx', import ListingPages from 'listing/pages.jsx'
'listing/header.jsx', import ListingSearch from 'listing/search.jsx'
'listing/pages.jsx', import ListingGroups from 'listing/groups.jsx'
'listing/search.jsx', import ListingFilters from 'listing/filters.jsx'
'listing/groups.jsx',
'listing/filters.jsx'
],
function(
MailPoet,
jQuery,
React,
Router,
classNames,
ListingBulkActions,
ListingHeader,
ListingPages,
ListingSearch,
ListingGroups,
ListingFilters
) {
var Link = Router.Link;
var ListingItem = React.createClass({ const ListingItem = React.createClass({
getInitialState: function() { getInitialState: function() {
return { return {
toggled: true toggled: true
@@ -150,8 +133,10 @@ define(
); );
} }
let actions;
if (this.props.group === 'trash') { if (this.props.group === 'trash') {
var actions = ( actions = (
<div> <div>
<div className="row-actions"> <div className="row-actions">
<span> <span>
@@ -183,7 +168,7 @@ define(
</div> </div>
); );
} else { } else {
var actions = ( actions = (
<div> <div>
<div className="row-actions"> <div className="row-actions">
{ item_actions } { item_actions }
@@ -197,7 +182,7 @@ define(
); );
} }
var row_classes = classNames({ 'is-expanded': !this.state.toggled }) const row_classes = classNames({ 'is-expanded': !this.state.toggled });
return ( return (
<tr className={ row_classes }> <tr className={ row_classes }>
@@ -209,7 +194,7 @@ define(
}); });
var ListingItems = React.createClass({ const ListingItems = React.createClass({
render: function() { render: function() {
if (this.props.items.length === 0) { if (this.props.items.length === 0) {
return ( return (
@@ -231,8 +216,7 @@ define(
</tbody> </tbody>
); );
} else { } else {
const select_all_classes = classNames(
var selectAllClasses = classNames(
'mailpoet_select_all', 'mailpoet_select_all',
{ 'mailpoet_hidden': ( { 'mailpoet_hidden': (
this.props.selection === false this.props.selection === false
@@ -243,7 +227,7 @@ define(
return ( return (
<tbody> <tbody>
<tr className={ selectAllClasses }> <tr className={ select_all_classes }>
<td colSpan={ <td colSpan={
this.props.columns.length this.props.columns.length
+ (this.props.is_selectable ? 1 : 0) + (this.props.is_selectable ? 1 : 0)
@@ -294,7 +278,7 @@ define(
} }
}); });
var Listing = React.createClass({ const Listing = React.createClass({
contextTypes: { contextTypes: {
router: React.PropTypes.object.isRequired router: React.PropTypes.object.isRequired
}, },
@@ -305,8 +289,8 @@ define(
page: 1, page: 1,
count: 0, count: 0,
limit: 10, limit: 10,
sort_by: 'id', sort_by: null,
sort_order: 'desc', sort_order: null,
items: [], items: [],
groups: [], groups: [],
group: 'all', group: 'all',
@@ -332,13 +316,13 @@ define(
} }
}, },
getParam: function(param) { getParam: function(param) {
var regex = /(.*)\[(.*)\]/ const regex = /(.*)\[(.*)\]/;
var matches = regex.exec(param) const matches = regex.exec(param);
return [matches[1], matches[2]] return [matches[1], matches[2]]
}, },
initWithParams: function(params) { initWithParams: function(params) {
let state = this.state || {} let state = this.state || {};
let original_state = state let original_state = state;
// check for url params // check for url params
if (params.splat !== undefined) { if (params.splat !== undefined) {
params.splat.split('/').map(param => { params.splat.split('/').map(param => {
@@ -350,27 +334,38 @@ define(
let [k, v] = pair.split('=') let [k, v] = pair.split('=')
filters[k] = v filters[k] = v
} }
) );
state.filter = filters state.filter = filters;
break; break;
default: default:
state[key] = value state[key] = value;
} }
}) });
} }
// default overrides // defaults override
// limit per page
if (this.props.limit !== undefined) { if (this.props.limit !== undefined) {
state.limit = Math.abs(~~this.props.limit); state.limit = Math.abs(~~this.props.limit);
} }
// sort by
if (state.sort_by === null && this.props.sort_by !== undefined) {
state.sort_by = this.props.sort_by;
}
// sort order
if (state.sort_order === null && this.props.sort_order !== undefined) {
state.sort_order = this.props.sort_order;
}
this.setState(state, function() { this.setState(state, function() {
this.getItems(); this.getItems();
}.bind(this)); }.bind(this));
}, },
setParams: function() { setParams: function() {
var params = Object.keys(this.state) let params = Object.keys(this.state)
.filter(key => { .filter(key => {
return ( return (
[ [
@@ -397,7 +392,8 @@ define(
}) })
.filter(key => { return (key !== undefined) }) .filter(key => { return (key !== undefined) })
.join('/'); .join('/');
params = '/'+params
params = '/' + params;
if (this.props.location) { if (this.props.location) {
if (this.props.location.pathname !== params) { if (this.props.location.pathname !== params) {
@@ -408,7 +404,7 @@ define(
componentDidMount: function() { componentDidMount: function() {
if (this.isMounted()) { if (this.isMounted()) {
const params = this.props.params || {} const params = this.props.params || {}
this.initWithParams(params) this.initWithParams(params);
if (this.props.auto_refresh) { if (this.props.auto_refresh) {
jQuery(document).on('heartbeat-tick.mailpoet', function(e, data) { jQuery(document).on('heartbeat-tick.mailpoet', function(e, data) {
@@ -568,7 +564,6 @@ define(
selected_ids: [] selected_ids: []
}, function() { }, function() {
this.setParams(); this.setParams();
this.getItems();
}.bind(this)); }.bind(this));
}, },
handleSort: function(sort_by, sort_order = 'asc') { handleSort: function(sort_by, sort_order = 'asc') {
@@ -577,7 +572,6 @@ define(
sort_order: sort_order, sort_order: sort_order,
}, function() { }, function() {
this.setParams(); this.setParams();
this.getItems();
}.bind(this)); }.bind(this));
}, },
handleSelectItem: function(id, is_checked) { handleSelectItem: function(id, is_checked) {
@@ -637,7 +631,6 @@ define(
page: 1 page: 1
}, function() { }, function() {
this.setParams(); this.setParams();
this.getItems();
}.bind(this)); }.bind(this));
}, },
handleGroup: function(group) { handleGroup: function(group) {
@@ -651,7 +644,6 @@ define(
page: 1 page: 1
}, function() { }, function() {
this.setParams(); this.setParams();
this.getItems();
}.bind(this)); }.bind(this));
}, },
handleSetPage: function(page) { handleSetPage: function(page) {
@@ -661,29 +653,28 @@ define(
selected_ids: [] selected_ids: []
}, function() { }, function() {
this.setParams(); this.setParams();
this.getItems();
}.bind(this)); }.bind(this));
}, },
handleRenderItem: function(item, actions) { handleRenderItem: function(item, actions) {
var render = this.props.onRenderItem(item, actions); const render = this.props.onRenderItem(item, actions);
return render.props.children; return render.props.children;
}, },
handleRefreshItems: function() { handleRefreshItems: function() {
this.getItems(); this.getItems();
}, },
render: function() { render: function() {
var items = this.state.items, const items = this.state.items;
sort_by = this.state.sort_by, const sort_by = this.state.sort_by;
sort_order = this.state.sort_order; const sort_order = this.state.sort_order;
// columns // columns
var columns = this.props.columns || []; let columns = this.props.columns || [];
columns = columns.filter(function(column) { columns = columns.filter(function(column) {
return (column.display === undefined || !!(column.display) === true); return (column.display === undefined || !!(column.display) === true);
}); });
// bulk actions // bulk actions
var bulk_actions = this.props.bulk_actions || []; let bulk_actions = this.props.bulk_actions || [];
if (this.state.group === 'trash' && bulk_actions.length > 0) { if (this.state.group === 'trash' && bulk_actions.length > 0) {
bulk_actions = [ bulk_actions = [
@@ -701,9 +692,9 @@ define(
} }
// item actions // item actions
var item_actions = this.props.item_actions || []; const item_actions = this.props.item_actions || [];
var table_classes = classNames( const table_classes = classNames(
'mailpoet_listing_table', 'mailpoet_listing_table',
'wp-list-table', 'wp-list-table',
'widefat', 'widefat',
@@ -713,7 +704,7 @@ define(
); );
// search // search
var search = ( let search = (
<ListingSearch <ListingSearch
onSearch={ this.handleSearch } onSearch={ this.handleSearch }
search={ this.state.search } search={ this.state.search }
@@ -724,7 +715,7 @@ define(
} }
// groups // groups
var groups = ( let groups = (
<ListingGroups <ListingGroups
groups={ this.state.groups } groups={ this.state.groups }
group={ this.state.group } group={ this.state.group }
@@ -820,6 +811,4 @@ define(
} }
}); });
return Listing; module.exports = Listing;
}
);

View File

@@ -244,6 +244,8 @@ const SegmentList = React.createClass({
columns={ columns } columns={ columns }
bulk_actions={ bulk_actions } bulk_actions={ bulk_actions }
item_actions={ item_actions } item_actions={ item_actions }
sort_by="name"
sort_order="asc"
/> />
</div> </div>
); );

View File

@@ -27,8 +27,8 @@ class Handler {
// searching // searching
'search' => (isset($data['search']) ? $data['search'] : null), 'search' => (isset($data['search']) ? $data['search'] : null),
// sorting // sorting
'sort_by' => (isset($data['sort_by']) ? $data['sort_by'] : 'id'), 'sort_by' => (!empty($data['sort_by']) ? $data['sort_by'] : 'id'),
'sort_order' => (isset($data['sort_order']) ? $data['sort_order'] : 'asc'), 'sort_order' => (!empty($data['sort_order']) ? $data['sort_order'] : 'asc'),
// grouping // grouping
'group' => (isset($data['group']) ? $data['group'] : null), 'group' => (isset($data['group']) ? $data['group'] : null),
// filters // filters