Просмотр исходного кода

Merge pull request #4568 from weseek/feat/77551-show-pagination

Feat/77551 show pagination
Yohei Shiina 4 лет назад
Родитель
Сommit
5726b5d87e

+ 6 - 6
packages/app/src/components/PaginationWrapper.jsx

@@ -63,10 +63,10 @@ const PaginationWrapper = React.memo((props) => {
     if (activePage !== 1) {
       paginationItems.push(
         <PaginationItem key="painationItemFirst">
-          <PaginationLink first onClick={() => { return changePage(1) }} />
+          <PaginationLink first onClick={() => { return changePage != null && changePage(1) }} />
         </PaginationItem>,
         <PaginationItem key="painationItemPrevious">
-          <PaginationLink previous onClick={() => { return changePage(activePage - 1) }} />
+          <PaginationLink previous onClick={() => { return changePage != null && changePage(activePage - 1) }} />
         </PaginationItem>,
       );
     }
@@ -93,7 +93,7 @@ const PaginationWrapper = React.memo((props) => {
     for (let number = paginationStart; number <= maxViewPageNum; number++) {
       paginationItems.push(
         <PaginationItem key={`paginationItem-${number}`} active={number === activePage}>
-          <PaginationLink onClick={() => { return changePage(number) }}>
+          <PaginationLink onClick={() => { return changePage != null && changePage(number) }}>
             {number}
           </PaginationLink>
         </PaginationItem>,
@@ -112,10 +112,10 @@ const PaginationWrapper = React.memo((props) => {
     if (totalPage !== activePage) {
       paginationItems.push(
         <PaginationItem key="painationItemNext">
-          <PaginationLink next onClick={() => { return changePage(activePage + 1) }} />
+          <PaginationLink next onClick={() => { return changePage != null && changePage(activePage + 1) }} />
         </PaginationItem>,
         <PaginationItem key="painationItemLast">
-          <PaginationLink last onClick={() => { return changePage(totalPage) }} />
+          <PaginationLink last onClick={() => { return changePage != null && changePage(totalPage) }} />
         </PaginationItem>,
       );
     }
@@ -159,7 +159,7 @@ const PaginationWrapper = React.memo((props) => {
 
 PaginationWrapper.propTypes = {
   activePage: PropTypes.number.isRequired,
-  changePage: PropTypes.func.isRequired,
+  changePage: PropTypes.func,
   totalItemsCount: PropTypes.number.isRequired,
   pagingLimit: PropTypes.number,
   align: PropTypes.string,

+ 66 - 26
packages/app/src/components/SearchPage.jsx

@@ -32,16 +32,21 @@ class SearchPage extends React.Component {
       searchResultMeta: {},
       selectedPage: {},
       selectedPages: new Set(),
+      searchResultCount: 0,
+      activePage: 1,
+      pagingLimit: 3, // change to an appropriate limit number
       excludeUsersHome: true,
       excludeTrash: true,
     };
 
     this.changeURL = this.changeURL.bind(this);
     this.search = this.search.bind(this);
+    this.searchHandler = this.searchHandler.bind(this);
     this.selectPage = this.selectPage.bind(this);
     this.toggleCheckBox = this.toggleCheckBox.bind(this);
     this.onExcludeUsersHome = this.onExcludeUsersHome.bind(this);
     this.onExcludeTrash = this.onExcludeTrash.bind(this);
+    this.onPagingNumberChanged = this.onPagingNumberChanged.bind(this);
   }
 
   componentDidMount() {
@@ -96,13 +101,34 @@ class SearchPage extends React.Component {
     return query;
   }
 
-  search(data) {
+  /**
+   * this method is called when user changes paging number
+   */
+  async onPagingNumberChanged(activePage) {
+    // this.setState does not change the state immediately and following calls of this.search outside of this.setState will have old activePage state.
+    // To prevent above, pass this.search as a callback function to make sure this.search will have the latest activePage state.
+    this.setState({ activePage }, () => this.search({ keyword: this.state.searchedKeyword }));
+  }
+
+  /**
+   * this method is called when user searches by pressing Enter or using searchbox
+   */
+  async searchHandler(data) {
+    // this.setState does not change the state immediately and following calls of this.search outside of this.setState will have old activePage state.
+    // To prevent above, pass this.search as a callback function to make sure this.search will have the latest activePage state.
+    this.setState({ activePage: 1 }, () => this.search(data));
+  }
+
+  async search(data) {
     const keyword = data.keyword;
     if (keyword === '') {
       this.setState({
         searchingKeyword: '',
+        searchedKeyword: '',
         searchedPages: [],
         searchResultMeta: {},
+        searchResultCount: 0,
+        activePage: 1,
       });
 
       return true;
@@ -111,29 +137,40 @@ class SearchPage extends React.Component {
     this.setState({
       searchingKeyword: keyword,
     });
-    this.props.appContainer.apiGet('/search', { q: this.createSearchQuery(keyword) })
-      .then((res) => {
-        this.changeURL(keyword);
-        if (res.data.length > 0) {
-          this.setState({
-            searchedKeyword: keyword,
-            searchedPages: res.data,
-            searchResultMeta: res.meta,
-            selectedPage: res.data[0],
-          });
-        }
-        else {
-          this.setState({
-            searchedKeyword: keyword,
-            searchedPages: [],
-            searchResultMeta: {},
-            selectedPage: {},
-          });
-        }
-      })
-      .catch((err) => {
-        toastError(err);
+    const pagingLimit = this.state.pagingLimit;
+    const offset = (this.state.activePage * pagingLimit) - pagingLimit;
+    try {
+      const res = await this.props.appContainer.apiGet('/search', {
+        q: this.createSearchQuery(keyword),
+        limit: pagingLimit,
+        offset,
       });
+      this.changeURL(keyword);
+      if (res.data.length > 0) {
+        this.setState({
+          searchedKeyword: keyword,
+          searchedPages: res.data,
+          searchResultMeta: res.meta,
+          searchResultCount: res.meta.total,
+          selectedPage: res.data[0],
+          // reset active page if keyword changes, otherwise set the current state
+          activePage: this.state.searchedKeyword === keyword ? this.state.activePage : 1,
+        });
+      }
+      else {
+        this.setState({
+          searchedKeyword: keyword,
+          searchedPages: [],
+          searchResultMeta: {},
+          searchResultCount: 0,
+          selectedPage: {},
+          activePage: 1,
+        });
+      }
+    }
+    catch (err) {
+      toastError(err);
+    }
   }
 
   selectPage= (pageId) => {
@@ -172,10 +209,13 @@ class SearchPage extends React.Component {
         deletionMode={false}
         selectedPage={this.state.selectedPage}
         selectedPages={this.state.selectedPages}
+        searchResultCount={this.state.searchResultCount}
+        activePage={this.state.activePage}
+        pagingLimit={this.state.pagingLimit}
         onClickInvoked={this.selectPage}
         onChangedInvoked={this.toggleCheckBox}
-      >
-      </SearchResultList>
+        onPagingNumberChanged={this.onPagingNumberChanged}
+      />
     );
   }
 
@@ -184,7 +224,7 @@ class SearchPage extends React.Component {
       <SearchControl
         searchingKeyword={this.state.searchingKeyword}
         appContainer={this.props.appContainer}
-        onSearchInvoked={this.search}
+        onSearchInvoked={this.searchHandler}
         onExcludeUsersHome={this.onExcludeUsersHome}
         onExcludeTrash={this.onExcludeTrash}
       >

+ 31 - 12
packages/app/src/components/SearchPage/SearchResultList.jsx

@@ -1,22 +1,37 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import SearchResultListItem from './SearchResultListItem';
+import PaginationWrapper from '../PaginationWrapper';
 
 class SearchResultList extends React.Component {
 
   render() {
-    return this.props.pages.map((page) => {
-      // TODO : send cetain  length of body (revisionBody) from elastisearch by adding some settings to the query and
-      //         when keyword is not in page content, display revisionBody.
-      // TASK : https://estoc.weseek.co.jp/redmine/issues/79606
-      return (
-        <SearchResultListItem
-          page={page}
-          onClickInvoked={this.props.onClickInvoked}
-          noLink
-        />
-      );
-    });
+    return (
+      <>
+        {this.props.pages.map((page) => {
+        // TODO : send cetain length of body (revisionBody) from elastisearch by adding some settings to the query and
+        //         when keyword is not in page content, display revisionBody.
+        // TASK : https://estoc.weseek.co.jp/redmine/issues/79606
+          return (
+            <SearchResultListItem
+              page={page}
+              onClickInvoked={this.props.onClickInvoked}
+              noLink
+            />
+          );
+        })}
+        {this.props.searchResultCount != null && this.props.searchResultCount > 0 && (
+          <div className="my-4 mx-auto">
+            <PaginationWrapper
+              activePage={this.props.activePage}
+              changePage={this.props.onPagingNumberChanged}
+              totalItemsCount={this.props.searchResultCount || 0}
+              pagingLimit={this.props.pagingLimit}
+            />
+          </div>
+        )}
+      </>
+    );
   }
 
 }
@@ -25,8 +40,12 @@ SearchResultList.propTypes = {
   pages: PropTypes.array.isRequired,
   deletionMode: PropTypes.bool.isRequired,
   selectedPages: PropTypes.array.isRequired,
+  searchResultCount: PropTypes.number,
+  activePage: PropTypes.number.isRequired,
+  pagingLimit: PropTypes.number,
   onClickInvoked: PropTypes.func,
   onChangeInvoked: PropTypes.func,
+  onPagingNumberChanged: PropTypes.func,
 };
 
 export default SearchResultList;