#446 Share the API and JS pagination components that were in the Halp widget
Merged 6 years ago by abompard. Opened 6 years ago by abompard.
abompard/fedora-hubs feature/pagination-shared  into  develop

@@ -0,0 +1,79 @@ 

+ import React from 'react';

+ import PropTypes from 'prop-types';

+ import { defineMessages, FormattedMessage } from 'react-intl';

+ 

+ 

+ const messages = defineMessages({

+   pager_previous: {

+     id: "hubs.widgets.halp.pager.previous",

+     defaultMessage: "Previous"

+   },

+   pager_next: {

+     id: "hubs.widgets.halp.pager.next",

+     defaultMessage: "Next"

+   },

+   pager_status: {

+     id: "hubs.widgets.halp.pager.status",

+     defaultMessage: "Page {page} / {total}"

+   },

+ });

+ 

+ 

+ export default class Pagination extends React.Component {

+ 

+   constructor(props) {

+     super(props);

+     this.pageChange = this.pageChange.bind(this);

+     this.onPagePrevious = this.pageChange.bind(this, -1);

+     this.onPageNext = this.pageChange.bind(this, 1);

+   }

+ 

+   pageChange(inc, e) {

+     e.preventDefault();

+     const requestedPage = this.props.page.nr + inc;

+     this.props.onPageChange(requestedPage);

+   }

+ 

+   render() {

+     return (

+       <nav aria-label="Page navigation">

+         <ul className={`pagination ${this.props.className}`}>

+           <li className={"page-item" + (this.props.page.has_prev ? "" : " disabled")}>

+             <button className="btn btn-link page-link" value="-1" onClick={this.onPagePrevious}>

+               <span aria-hidden="true" className="mr-1">&larr;</span>

+               {this.props.previousText}

+             </button>

+           </li>

+           <li className="page-item disabled">

+             <span className="page-link">

+               <FormattedMessage

+                 {...messages.pager_status}

+                 values={{page: this.props.page.nr, total: this.props.page.total_pages}}

+                 />

+             </span>

+           </li>

+           <li className={"page-item" + (this.props.page.has_next ? "" : " disabled")}>

+             <button className="btn btn-link page-link" value="1" onClick={this.onPageNext}>

+               {this.props.nextText}

+               <span aria-hidden="true" className="ml-1">&rarr;</span>

+             </button>

+           </li>

+         </ul>

+       </nav>

+     );

+   }

+ }

+ Pagination.propTypes = {

+   className: PropTypes.string,

+   previousText: PropTypes.node,

+   nextText: PropTypes.node,

+ }

+ Pagination.defaultProps = {

+   className: "",

+   previousText: (

+     <FormattedMessage {...messages.pager_previous} />

+   ),

+   nextText: (

+     <FormattedMessage {...messages.pager_next} />

+   ),

+ }

@@ -3,6 +3,7 @@ 

  import CompletionInput from '../../components/CompletionInput';

  import Spinner from "../../components/Spinner";

  import Modal from '../../components/Modal';

+ import Pagination from "../../components/Pagination";

  import Request from './Request';

  

  
@@ -60,7 +61,7 @@ 

      };

      this.onSubmit = this.onSubmit.bind(this);

      this.loadFromServer = this.loadFromServer.bind(this);

-     this.onPageChange = this.onPageChange.bind(this);

+     this.changePage = this.changePage.bind(this);

      this.handleChange = this.handleChange.bind(this);

    }

  
@@ -81,10 +82,8 @@ 

      this.loadFromServer();

    }

  

-   onPageChange(e) {

-     const inc = parseInt(e.target.value);

-     const requestedPage = this.state.page.nr + inc;

-     this.loadFromServer(requestedPage);

+   changePage(pageNr) {

+     this.loadFromServer(pageNr);

    }

  

    loadFromServer(requestedPage) {
@@ -128,27 +127,17 @@ 

        footer = null;

      } else {

        footer = (

-         <nav aria-label="Page navigation">

-           <ul className="pagination">

-             <li className={"page-item" + (this.state.page.has_prev ? "" : " disabled")}>

-               <button className="btn btn-link page-link" value="-1" onClick={this.onPageChange}>

-                 <span aria-hidden="true">&larr;</span>

-                 <FormattedMessage {...messages.pager_newer} />

-               </button>

-             </li>

-             <FormattedMessage

-               tagName="li"

-               {...messages.pager_status}

-               values={{page: this.state.page.nr, total: this.state.page.total_pages}}

-               />

-             <li className={"page-item" + (this.state.page.has_next ? "" : " disabled")}>

-               <button className="btn btn-link page-link" value="1" onClick={this.onPageChange}>

-                 <FormattedMessage {...messages.pager_older} />

-                 <span aria-hidden="true">&rarr;</span>

-               </button>

-             </li>

-           </ul>

-         </nav>

+         <Pagination

+           page={this.state.page}

+           onPageChange={this.changePage}

+           className="pagination-sm"

+           previousText={

+             <FormattedMessage {...messages.pager_newer} />

+           }

+           nextText={

+             <FormattedMessage {...messages.pager_older} />

+           }

+           />

        );

      }

  

@@ -0,0 +1,99 @@ 

+ from __future__ import unicode_literals, absolute_import

+ 

+ from hubs.app import app

+ from hubs.utils.pagination import paginate  # circular

+ from hubs.tests import APPTest

+ 

+ 

+ class PaginateTestCase(APPTest):

+ 

+     def setUp(self):

+         super(PaginateTestCase, self).setUp()

+         self.paginate = paginate

+         self.values = ["A", "B", "C", "D"]

+ 

+     def test_base(self):

+         with app.test_request_context('/'):

+             result = self.paginate(self.values, 3)

+         self.assertListEqual(result[0], self.values[:3])

+         self.assertDictEqual(result[1], {

+             'has_next': True,

+             'has_prev': False,

+             'nr': 1,

+             'total_entries': len(self.values),

+             'total_pages': 2,

+             })

+ 

+     def test_page_2(self):

+         with app.test_request_context('/?page=2'):

+             result = self.paginate(self.values, 3)

+         self.assertListEqual(result[0], self.values[3:])

+         self.assertDictEqual(result[1], {

+             'has_next': False,

+             'has_prev': True,

+             'nr': 2,

+             'total_entries': len(self.values),

+             'total_pages': 2,

+             })

+ 

+     def test_page_0(self):

+         with app.test_request_context('/?page=0'):

+             result = self.paginate(self.values, 3)

+         self.assertListEqual(result[0], self.values[:3])

+         self.assertDictEqual(result[1], {

+             'has_next': True,

+             'has_prev': False,

+             'nr': 1,

+             'total_entries': len(self.values),

+             'total_pages': 2,

+             })

+ 

+     def test_invalid_page(self):

+         with app.test_request_context('/?page=blah'):

+             result = self.paginate(self.values, 3)

+         self.assertListEqual(result[0], self.values[:3])

+         self.assertDictEqual(result[1], {

+             'has_next': True,

+             'has_prev': False,

+             'nr': 1,

+             'total_entries': len(self.values),

+             'total_pages': 2,

+             })

+ 

+     def test_page_too_high(self):

+         with app.test_request_context('/?page=3'):

+             result = self.paginate(self.values, 3)

+         self.assertListEqual(result[0], self.values[3:])

+         self.assertDictEqual(result[1], {

+             'has_next': False,

+             'has_prev': True,

+             'nr': 2,

+             'total_entries': len(self.values),

+             'total_pages': 2,

+             })

+ 

+     def test_single_page(self):

+         values = self.values[:3]

+         with app.test_request_context('/'):

+             result = self.paginate(values, 3)

+         self.assertListEqual(result[0], values)

+         self.assertDictEqual(result[1], {

+             'has_next': False,

+             'has_prev': False,

+             'nr': 1,

+             'total_entries': len(values),

+             'total_pages': 1,

+             })

+ 

+     def test_no_value(self):

+         values = []

+         with app.test_request_context('/'):

+             result = self.paginate(values, 3)

+         self.assertListEqual(result[0], [])

+         self.assertDictEqual(result[1], {

+             'has_next': False,

+             'has_prev': False,

+             'nr': 1,

+             'total_entries': 0,

+             'total_pages': 1,

+             })

@@ -390,98 +390,3 @@ 

                 }

          result = self.func.should_invalidate(msg)

          self.assertTrue(result)

- 

- 

- class PaginateTestCase(WidgetTest):

- 

-     def setUp(self):

-         super(PaginateTestCase, self).setUp()

-         from hubs.widgets.halp.utils import paginate  # circular

-         self.paginate = paginate

-         self.values = ["A", "B", "C", "D"]

- 

-     def test_base(self):

-         with app.test_request_context('/'):

-             result = self.paginate(self.values, 3)

-         self.assertListEqual(result[0], self.values[:3])

-         self.assertDictEqual(result[1], {

-             'has_next': True,

-             'has_prev': False,

-             'nr': 1,

-             'total_entries': len(self.values),

-             'total_pages': 2,

-             })

- 

-     def test_page_2(self):

-         with app.test_request_context('/?page=2'):

-             result = self.paginate(self.values, 3)

-         self.assertListEqual(result[0], self.values[3:])

-         self.assertDictEqual(result[1], {

-             'has_next': False,

-             'has_prev': True,

-             'nr': 2,

-             'total_entries': len(self.values),

-             'total_pages': 2,

-             })

- 

-     def test_page_0(self):

-         with app.test_request_context('/?page=0'):

-             result = self.paginate(self.values, 3)

-         self.assertListEqual(result[0], self.values[:3])

-         self.assertDictEqual(result[1], {

-             'has_next': True,

-             'has_prev': False,

-             'nr': 1,

-             'total_entries': len(self.values),

-             'total_pages': 2,

-             })

- 

-     def test_invalid_page(self):

-         with app.test_request_context('/?page=blah'):

-             result = self.paginate(self.values, 3)

-         self.assertListEqual(result[0], self.values[:3])

-         self.assertDictEqual(result[1], {

-             'has_next': True,

-             'has_prev': False,

-             'nr': 1,

-             'total_entries': len(self.values),

-             'total_pages': 2,

-             })

- 

-     def test_page_too_high(self):

-         with app.test_request_context('/?page=3'):

-             result = self.paginate(self.values, 3)

-         self.assertListEqual(result[0], self.values[3:])

-         self.assertDictEqual(result[1], {

-             'has_next': False,

-             'has_prev': True,

-             'nr': 2,

-             'total_entries': len(self.values),

-             'total_pages': 2,

-             })

- 

-     def test_single_page(self):

-         values = self.values[:3]

-         with app.test_request_context('/'):

-             result = self.paginate(values, 3)

-         self.assertListEqual(result[0], values)

-         self.assertDictEqual(result[1], {

-             'has_next': False,

-             'has_prev': False,

-             'nr': 1,

-             'total_entries': len(values),

-             'total_pages': 1,

-             })

- 

-     def test_no_value(self):

-         values = []

-         with app.test_request_context('/'):

-             result = self.paginate(values, 3)

-         self.assertListEqual(result[0], [])

-         self.assertDictEqual(result[1], {

-             'has_next': False,

-             'has_prev': False,

-             'nr': 1,

-             'total_entries': 0,

-             'total_pages': 1,

-             })

@@ -0,0 +1,60 @@ 

+ from __future__ import unicode_literals

+ 

+ import flask

+ 

+ 

+ def paginate(values, per_page):

+     """Paginate the values.

+ 

+     The requested page will be extracted from the ``page`` query string

+     element.

+ 

+     The returned value will be a tuple with two elements:

+ 

+     - the paginated values

+     - the page information

+ 

+     The page information is a dictionary with the following keys:

+ 

+     - ``nr``: the page number

+     - ``has_prev``: ``True`` if there is a previous page, ``False`` otherwise

+     - ``has_next``: ``True`` if there is a following page, ``False`` otherwise

+     - ``total_entries``: the total number of entries in the ``values`` argument

+     - ``total_pages``: the total number of pages

+ 

+     Args:

+         values (list or query): The list or SQLAlchemy query object to

+             paginate.

+         per_page (int): the number of elements per page.

+ 

+     Returns:

+         tuple: a tuple containing the paginated values and the page

+             information.

+     """

+     try:

+         total = values.count()

+     except TypeError:

+         total = len(values)

+     try:

+         page = flask.request.values.get("page", 1, int)

+     except ValueError:

+         page = 1

+     last_page = int(total / per_page)

+     if total > last_page * per_page:

+         last_page += 1

+     if last_page < 1:

+         last_page = 1

+     if page < 1:

+         page = 1

+     if page > last_page:

+         page = last_page

+     start = (page - 1) * per_page

+     end = start + per_page

+     page_data = {

+         "nr": page,

+         "has_prev": page > 1,

+         "has_next": page < last_page,

+         "total_entries": total,

+         "total_pages": last_page,

+     }

+     return values[start:end], page_data

@@ -1,6 +1,5 @@ 

  from __future__ import unicode_literals

  

- import flask

  

  from hubs.models import Hub, HubConfig

  
@@ -21,60 +20,3 @@ 

              HubConfig.chat_channel == msg["channel"]

          ).values(Hub.name)

          ]

- 

- 

- def paginate(values, per_page):

-     """Paginate the values.

- 

-     The requested page will be extracted from the ``page`` query string

-     element.

- 

-     The returned value will be a tuple with two elements:

- 

-     - the paginated values

-     - the page information

- 

-     The page information is a dictionary with the following keys:

- 

-     - ``nr``: the page number

-     - ``has_prev``: ``True`` if there is a previous page, ``False`` otherwise

-     - ``has_next``: ``True`` if there is a following page, ``False`` otherwise

-     - ``total_entries``: the total number of entries in the ``values`` argument

-     - ``total_pages``: the total number of pages

- 

-     Args:

-         values (list or query): The list or SQLAlchemy query object to

-             paginate.

-         per_page (int): the number of elements per page.

- 

-     Returns:

-         tuple: a tuple containing the paginated values and the page

-             information.

-     """

-     try:

-         total = values.count()

-     except TypeError:

-         total = len(values)

-     try:

-         page = flask.request.values.get("page", 1, int)

-     except ValueError:

-         page = 1

-     last_page = int(total / per_page)

-     if total > last_page * per_page:

-         last_page += 1

-     if last_page < 1:

-         last_page = 1

-     if page < 1:

-         page = 1

-     if page > last_page:

-         page = last_page

-     start = (page - 1) * per_page

-     end = start + per_page

-     page_data = {

-         "nr": page,

-         "has_prev": page > 1,

-         "has_next": page < last_page,

-         "total_entries": total,

-         "total_pages": last_page,

-     }

-     return values[start:end], page_data

file modified
+2 -1
@@ -6,9 +6,10 @@ 

  import flask

  

  from hubs.models import Hub

+ from hubs.utils.pagination import paginate

  from hubs.widgets.view import WidgetView

  from .functions import GetRequests

- from .utils import find_hubs_for_msg, paginate

+ from .utils import find_hubs_for_msg

  

  try:

      from flask.signals import before_render_template