@plone/volto 17.0.0-alpha.4 → 17.0.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.changelog.draft CHANGED
@@ -1,33 +1,13 @@
1
- ## 17.0.0-alpha.4 (2023-04-12)
2
-
3
- ### Feature
4
-
5
- - DefaultView (view of fields for content types with blocks disabled): Show field name as tip on hover of label. @ksuess [#4598](https://github.com/plone/volto/issues/4598)
6
- - Support RelationList field with named StaticCatalogVocabulary and SelectWidget. @ksuess [#4614](https://github.com/plone/volto/issues/4614)
7
- - Support for declaring a theme in `volto.config.js` or in `package.json`
8
- Add two entry points to allow extension of a theme from other add-ons. @sneridagh [#4625](https://github.com/plone/volto/issues/4625)
9
- - Set sameSite in I18N_LANGUAGE cookie @sneridagh [#4627](https://github.com/plone/volto/issues/4627)
10
- - Added querystring search get option. @robgietema [#4658](https://github.com/plone/volto/issues/4658)
1
+ ## 17.0.0-alpha.5 (2023-04-14)
11
2
 
12
3
  ### Bugfix
13
4
 
14
- - Added current page parameter to route in listing and search block pagination - Fix: #3868 @bipoza [#4159](https://github.com/plone/volto/issues/4159)
15
- - Fix regexp that checks valid URLs and improve tests [cekk] [#4601](https://github.com/plone/volto/issues/4601)
16
- - Fixed wrong localization on password reset page @iRohitSingh [#4656](https://github.com/plone/volto/issues/4656)
17
- - fix sitemap.xml.gz not is not compressed @dobri1408 [#4663](https://github.com/plone/volto/issues/4663)
18
-
19
- ### Internal
20
-
21
- - Trigger CI workflows to run from external pull requests. @davisagli [#4629](https://github.com/plone/volto/issues/4629)
22
- - Update to p.restapi 8.36.0 and Plone 6.0.3 @sneridagh [#4682](https://github.com/plone/volto/issues/4682)
5
+ - Generate a split sitemap @reebalazs [#4638](https://github.com/plone/volto/issues/4638)
6
+ - Fix Move to top of folder ordering in folder content view @iFlameing [#4690](https://github.com/plone/volto/issues/4690)
7
+ - Revert "Add current page parameter to the route in the listing and search block pagination (#4159)" @sneridagh [#4695](https://github.com/plone/volto/issues/4695)
8
+ - Fix search block in edit mode re-queries multiple blocks with an empty search text @reebalazs [#4697](https://github.com/plone/volto/issues/4697)
23
9
 
24
10
  ### Documentation
25
11
 
26
- - Added `JavaScript` and `NodeJS` as accepted spellings, and deviations of them as rejected spellings. @utkkkarshhh [#3092](https://github.com/plone/volto/issues/3092)
27
- - Fix documentation build, add pins @sneridagh [#4626](https://github.com/plone/volto/issues/4626)
28
- - Update Volto contributing to align with and refer to the new Plone core code contributing requirements. @stevepiercy [#4634](https://github.com/plone/volto/issues/4634)
29
- - Improve creating views documentation page. @rboixaderg [#4636](https://github.com/plone/volto/issues/4636)
30
- - Razzle upgrade notice in upgrade guide @sneridagh [#4641](https://github.com/plone/volto/issues/4641)
31
- - Rename "Developer Guidelines" to "Contributing". @stevepiercy [#4666](https://github.com/plone/volto/issues/4666)
32
- - Fix broken link to `ReactJS.org`. @stevepiercy [#4667](https://github.com/plone/volto/issues/4667)
12
+ - Update links for 2022 Training archive. @stevepiercy [#4635](https://github.com/plone/volto/issues/4635)
33
13
 
package/CHANGELOG.md CHANGED
@@ -8,6 +8,20 @@
8
8
 
9
9
  <!-- towncrier release notes start -->
10
10
 
11
+ ## 17.0.0-alpha.5 (2023-04-14)
12
+
13
+ ### Bugfix
14
+
15
+ - Generate a split sitemap @reebalazs [#4638](https://github.com/plone/volto/issues/4638)
16
+ - Fix Move to top of folder ordering in folder content view @iFlameing [#4690](https://github.com/plone/volto/issues/4690)
17
+ - Revert "Add current page parameter to the route in the listing and search block pagination (#4159)" @sneridagh [#4695](https://github.com/plone/volto/issues/4695)
18
+ - Fix search block in edit mode re-queries multiple blocks with an empty search text @reebalazs [#4697](https://github.com/plone/volto/issues/4697)
19
+
20
+ ### Documentation
21
+
22
+ - Update links for 2022 Training archive. @stevepiercy [#4635](https://github.com/plone/volto/issues/4635)
23
+
24
+
11
25
  ## 17.0.0-alpha.4 (2023-04-12)
12
26
 
13
27
  ### Feature
package/README.md CHANGED
@@ -186,19 +186,16 @@ You can find the latest (in-progress) documentation in [https://6.dev-docs.plone
186
186
 
187
187
  ## Training
188
188
 
189
- On the [Plone Trainings Website](https://training.plone.org) you'll find
190
- Volto-dedicated open training materials, plus React and other
191
- JavaScript-centered trainings.
189
+ On the [Plone Training website](https://training.plone.org), you'll find Volto-dedicated training materials, plus other JavaScript-centered trainings.
192
190
 
193
191
  - [Mastering Plone 6 Development](https://training.plone.org/mastering-plone/)
194
192
  The comprehensive training on Plone 6 with best practice tips for developers and integrators.
195
- - [Volto](https://training.plone.org/5/volto/index.html)
196
- A detailed training on how to create your own website using Volto frontend.
197
193
  - [Volto Hands-On](https://training.plone.org/voltohandson/index.html)
198
194
  - [Volto Add-ons Development](https://training.plone.org/voltoaddons/index.html)
199
- - [Plone Deployment](https://training.plone.org/5/plone-deployment/index.html)
200
- - [React](https://training.plone.org/react/index.html)
201
- - [JavaScript For Plone Developers](https://training.plone.org/5/javascript/index.html)
195
+ - [Effective Volto](https://training.plone.org/effective-volto/index.html)
196
+ - [Plone Deployment](https://training.plone.org/plone-deployment/index.html)
197
+ - [Volto](https://2022.training.plone.org/volto/index.html) (archived)
198
+ - [JavaScript For Plone Developers](https://2022.training.plone.org/javascript/index.html) (archived)
202
199
 
203
200
  ## Talks
204
201
 
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "17.0.0-alpha.4",
12
+ "version": "17.0.0-alpha.5",
13
13
  "repository": {
14
14
  "type": "git",
15
15
  "url": "git@github.com:plone/volto.git"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plone/volto-slate",
3
- "version": "17.0.0-alpha.4",
3
+ "version": "17.0.0-alpha.5",
4
4
  "description": "Slate.js integration with Volto",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -36,26 +36,6 @@ test('renders a ListingBody component', () => {
36
36
  content: {
37
37
  data: {
38
38
  is_folderish: true,
39
- blocks: {
40
- '839ee00b-013b-4f4a-9b10-8867938fdac3': {
41
- '@type': 'listing',
42
- block: '839ee00b-013b-4f4a-9b10-8867938fdac3',
43
- headlineTag: 'h2',
44
- query: [],
45
- querystring: {
46
- b_size: '2',
47
- query: [
48
- {
49
- i: 'path',
50
- o: 'plone.app.querystring.operation.string.absolutePath',
51
- v: '/',
52
- },
53
- ],
54
- sort_order: 'ascending',
55
- },
56
- variation: 'default',
57
- },
58
- },
59
39
  },
60
40
  },
61
41
  intl: {
@@ -25,7 +25,7 @@ export default function withQuerystringResults(WrappedComponent) {
25
25
  const [initialPath] = React.useState(getBaseUrl(path));
26
26
 
27
27
  const copyFields = ['limit', 'query', 'sort_on', 'sort_order', 'depth'];
28
- const { currentPage, setCurrentPage } = usePagination(data.block, 1);
28
+
29
29
  const adaptedQuery = Object.assign(
30
30
  variation?.fullobjects ? { fullobjects: 1 } : { metadata_fields: '_all' },
31
31
  {
@@ -37,6 +37,7 @@ export default function withQuerystringResults(WrappedComponent) {
37
37
  : {},
38
38
  ),
39
39
  );
40
+ const { currentPage, setCurrentPage } = usePagination(querystring, 1);
40
41
  const querystringResults = useSelector(
41
42
  (state) => state.querystringsearch.subrequests,
42
43
  );
@@ -1,5 +1,4 @@
1
- import React from 'react';
2
- import useDeepCompareEffect from 'use-deep-compare-effect';
1
+ import React, { useEffect } from 'react';
3
2
  import { defineMessages } from 'react-intl';
4
3
  import { compose } from 'redux';
5
4
 
@@ -60,9 +59,11 @@ const SearchBlockEdit = (props) => {
60
59
  };
61
60
 
62
61
  const { query = {} } = data || {};
63
- useDeepCompareEffect(() => {
62
+ // We don't need deep compare here, as this is just json serializable data.
63
+ const deepQuery = JSON.stringify(query);
64
+ useEffect(() => {
64
65
  onTriggerSearch();
65
- }, [query, onTriggerSearch]);
66
+ }, [deepQuery, onTriggerSearch]);
66
67
 
67
68
  return (
68
69
  <>
@@ -114,15 +114,19 @@ function normalizeState({
114
114
  block: id,
115
115
  };
116
116
 
117
- // TODO: need to check if SearchableText facet is not already in the query
118
- // Ideally the searchtext functionality should be restructured as being just
119
- // another facet
120
- params.query = params.query.reduce(
121
- // Remove SearchableText from query
122
- (acc, kvp) => (kvp.i === 'SearchableText' ? acc : [...acc, kvp]),
123
- [],
124
- );
117
+ // Note Ideally the searchtext functionality should be restructured as being just
118
+ // another facet. But right now it's the same. This means that if a searchText
119
+ // is provided, it will override the SearchableText facet.
120
+ // If there is no searchText, the SearchableText in the query remains in effect.
121
+ // TODO eventually the searchText should be a distinct facet from SearchableText, and
122
+ // the two conditions could be combined, in comparison to the current state, when
123
+ // one overrides the other.
125
124
  if (searchText) {
125
+ params.query = params.query.reduce(
126
+ // Remove SearchableText from query
127
+ (acc, kvp) => (kvp.i === 'SearchableText' ? acc : [...acc, kvp]),
128
+ [],
129
+ );
126
130
  params.query.push({
127
131
  i: 'SearchableText',
128
132
  o: 'plone.app.querystring.operation.string.contains',
@@ -144,16 +148,12 @@ const getSearchFields = (searchData) => {
144
148
  };
145
149
 
146
150
  /**
147
- * A hook that will mirror the search block state to a hash location
151
+ * A HOC that will mirror the search block state to a hash location
148
152
  */
149
153
  const useHashState = () => {
150
154
  const location = useLocation();
151
155
  const history = useHistory();
152
156
 
153
- /**
154
- * Required to maintain parameter compatibility.
155
- With this we will maintain support for receiving hash (#) and search (?) type parameters.
156
- */
157
157
  const oldState = React.useMemo(() => {
158
158
  return {
159
159
  ...qs.parse(location.search),
@@ -169,7 +169,7 @@ const useHashState = () => {
169
169
 
170
170
  const setSearchData = React.useCallback(
171
171
  (searchData) => {
172
- const newParams = qs.parse(location.search);
172
+ const newParams = qs.parse(location.hash);
173
173
 
174
174
  let changed = false;
175
175
 
@@ -186,11 +186,11 @@ const useHashState = () => {
186
186
 
187
187
  if (changed) {
188
188
  history.push({
189
- search: qs.stringify(newParams),
189
+ hash: qs.stringify(newParams),
190
190
  });
191
191
  }
192
192
  },
193
- [history, oldState, location.search],
193
+ [history, oldState, location.hash],
194
194
  );
195
195
 
196
196
  return [current, setSearchData];
@@ -282,8 +282,14 @@ const withSearch = (options) => (WrappedComponent) => {
282
282
  const timeoutRef = React.useRef();
283
283
  const facetSettings = data?.facets;
284
284
 
285
+ const deepQuery = JSON.stringify(data.query);
285
286
  const onTriggerSearch = React.useCallback(
286
- (toSearchText, toSearchFacets, toSortOn, toSortOrder) => {
287
+ (
288
+ toSearchText = undefined,
289
+ toSearchFacets = undefined,
290
+ toSortOn = undefined,
291
+ toSortOrder = undefined,
292
+ ) => {
287
293
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
288
294
  timeoutRef.current = setTimeout(
289
295
  () => {
@@ -291,7 +297,7 @@ const withSearch = (options) => (WrappedComponent) => {
291
297
  id,
292
298
  query: data.query || {},
293
299
  facets: toSearchFacets || facets,
294
- searchText: toSearchText,
300
+ searchText: toSearchText || searchText,
295
301
  sortOn: toSortOn || sortOn,
296
302
  sortOrder: toSortOrder || sortOrder,
297
303
  facetSettings,
@@ -305,11 +311,14 @@ const withSearch = (options) => (WrappedComponent) => {
305
311
  toSearchFacets ? inputDelay / 3 : inputDelay,
306
312
  );
307
313
  },
314
+ // eslint-disable-next-line react-hooks/exhaustive-deps
308
315
  [
309
- data.query,
316
+ // Use deep comparison of data.query
317
+ deepQuery,
310
318
  facets,
311
319
  id,
312
320
  setLocationSearchData,
321
+ searchText,
313
322
  sortOn,
314
323
  sortOrder,
315
324
  facetSettings,
@@ -797,17 +797,20 @@ class Contents extends Component {
797
797
  onMoveToTop(event, { value }) {
798
798
  const id = this.state.items[value]['@id'];
799
799
  value = this.state.currentPage * this.state.pageSize + value;
800
- this.props.orderContent(
801
- getBaseUrl(this.props.pathname),
802
- id.replace(/^.*\//, ''),
803
- -value,
804
- );
805
- this.setState(
806
- {
807
- currentPage: 0,
808
- },
809
- () => this.fetchContents(),
810
- );
800
+ this.props
801
+ .orderContent(
802
+ getBaseUrl(this.props.pathname),
803
+ id.replace(/^.*\//, ''),
804
+ -value,
805
+ )
806
+ .then(() => {
807
+ this.setState(
808
+ {
809
+ currentPage: 0,
810
+ },
811
+ () => this.fetchContents(),
812
+ );
813
+ });
811
814
  }
812
815
 
813
816
  /**
@@ -1,11 +1,34 @@
1
1
  import express from 'express';
2
- import { generateSitemap } from '@plone/volto/helpers/Sitemap/Sitemap';
2
+ import {
3
+ generateSitemap,
4
+ generateSitemapIndex,
5
+ SITEMAP_BATCH_SIZE,
6
+ } from '@plone/volto/helpers/Sitemap/Sitemap';
3
7
 
4
8
  export const sitemap = function (req, res, next) {
5
- generateSitemap(req).then((sitemap) => {
9
+ let start = 0;
10
+ let size = undefined;
11
+ const { batch: batchStr } = req.params;
12
+ if (batchStr !== undefined) {
13
+ const batch = parseInt(batchStr);
14
+ if (isNaN(batch) || batch === 0 || '' + batch !== batchStr) {
15
+ res.status(404);
16
+ // Some data, such as the internal API address, may be sensitive to be published
17
+ res.send(
18
+ `Invalid sitemap name, use sitemap.xml.gz, or batched sitemapN.xml.gz where N is a positive integer.`,
19
+ );
20
+ return;
21
+ }
22
+ start = SITEMAP_BATCH_SIZE * (batch - 1);
23
+ size = SITEMAP_BATCH_SIZE;
24
+ }
25
+ generateSitemap(req, start, size).then((sitemap) => {
6
26
  if (Buffer.isBuffer(sitemap)) {
7
27
  res.set('Content-Type', 'application/x-gzip');
8
- res.set('Content-Disposition', 'attachment; filename="sitemap.xml.gz"');
28
+ res.set(
29
+ 'Content-Disposition',
30
+ `attachment; filename="sitemap${batchStr || ''}.xml.gz"`,
31
+ );
9
32
  res.send(sitemap);
10
33
  } else {
11
34
  // {"errno":-111, "code":"ECONNREFUSED", "host": ...}
@@ -16,10 +39,20 @@ export const sitemap = function (req, res, next) {
16
39
  });
17
40
  };
18
41
 
42
+ export const sitemapIndex = function (req, res, next) {
43
+ generateSitemapIndex(req).then((sitemapIndex) => {
44
+ res.set('Content-Type', 'application/xml');
45
+ res.set('Content-Disposition', 'attachment; filename="sitemap-index.xml"');
46
+ res.send(sitemapIndex);
47
+ });
48
+ };
49
+
19
50
  export default function () {
20
51
  const middleware = express.Router();
21
52
 
22
53
  middleware.all('**/sitemap.xml.gz', sitemap);
54
+ middleware.all('**/sitemap:batch.xml.gz', sitemap);
55
+ middleware.all('**/sitemap-index.xml', sitemapIndex);
23
56
  middleware.id = 'sitemap.xml.gz';
24
57
  return middleware;
25
58
  }
@@ -15,12 +15,9 @@ import { addHeadersFactory } from '@plone/volto/helpers/Proxy/Proxy';
15
15
  */
16
16
  export const generateRobots = (req) =>
17
17
  new Promise((resolve) => {
18
- //const url = `${req.protocol}://${req.get('Host')}`;
19
- const request = superagent.get(
20
- `${
21
- config.settings.internalApiPath ?? config.settings.apiPath
22
- }/robots.txt`,
23
- );
18
+ const internalUrl =
19
+ config.settings.internalApiPath ?? config.settings.apiPath;
20
+ const request = superagent.get(`${internalUrl}/robots.txt`);
24
21
  request.set('Accept', 'text/plain');
25
22
  const authToken = req.universalCookies.get('auth_token');
26
23
  if (authToken) {
@@ -31,6 +28,12 @@ export const generateRobots = (req) =>
31
28
  if (error) {
32
29
  resolve(text || error);
33
30
  } else {
31
+ // Plone has probably returned the sitemap link with the internal url.
32
+ // If so, let's replace it with the current one.
33
+ const url = `${req.protocol}://${req.get('Host')}`;
34
+ text = text.replace(internalUrl, url);
35
+ // Replace the sitemap with the sitemap index.
36
+ text = text.replace('sitemap.xml.gz', 'sitemap-index.xml');
34
37
  resolve(text);
35
38
  }
36
39
  });
@@ -11,19 +11,23 @@ import { addHeadersFactory } from '@plone/volto/helpers/Proxy/Proxy';
11
11
 
12
12
  import config from '@plone/volto/registry';
13
13
 
14
+ export const SITEMAP_BATCH_SIZE = 5000;
15
+
14
16
  /**
15
17
  * Generate sitemap
16
18
  * @function generateSitemap
17
19
  * @param {Object} _req Request object
18
20
  * @return {string} Generated sitemap
19
21
  */
20
- export const generateSitemap = (_req) =>
22
+ export const generateSitemap = (_req, start = 0, size = undefined) =>
21
23
  new Promise((resolve) => {
22
24
  const { settings } = config;
23
25
  const APISUFIX = settings.legacyTraverse ? '' : '/++api++';
24
26
  const apiPath = settings.internalApiPath ?? settings.apiPath;
25
27
  const request = superagent.get(
26
- `${apiPath}${APISUFIX}/@search?metadata_fields=modified&b_size=100000000&use_site_search_settings=1`,
28
+ `${apiPath}${APISUFIX}/@search?metadata_fields=modified&b_start=${start}&b_size=${
29
+ size !== undefined ? size : 100000000
30
+ }&use_site_search_settings=1`,
27
31
  );
28
32
  request.set('Accept', 'application/json');
29
33
  request.use(addHeadersFactory(_req));
@@ -50,3 +54,41 @@ export const generateSitemap = (_req) =>
50
54
  }
51
55
  });
52
56
  });
57
+
58
+ /**
59
+ * Generate sitemap
60
+ * @function generateSitemapIndex
61
+ * @param {Object} _req Request object
62
+ * @return {string} Generated sitemap index
63
+ */
64
+ export const generateSitemapIndex = (_req) =>
65
+ new Promise((resolve) => {
66
+ const { settings } = config;
67
+ const APISUFIX = settings.legacyTraverse ? '' : '/++api++';
68
+ const apiPath = settings.internalApiPath ?? settings.apiPath;
69
+ const request = superagent.get(
70
+ `${apiPath}${APISUFIX}/@search?metadata_fields=modified&b_size=0&use_site_search_settings=1`,
71
+ );
72
+ request.set('Accept', 'application/json');
73
+ const authToken = _req.universalCookies.get('auth_token');
74
+ if (authToken) {
75
+ request.set('Authorization', `Bearer ${authToken}`);
76
+ }
77
+ request.end((error, { body } = {}) => {
78
+ if (error) {
79
+ resolve(body || error);
80
+ } else {
81
+ const items = Array.from(
82
+ { length: Math.ceil(body.items_total / SITEMAP_BATCH_SIZE) },
83
+ (_, i) =>
84
+ ` <sitemap>
85
+ <loc>${toPublicURL('/sitemap' + (i + 1) + '.xml.gz')}</loc>
86
+ </sitemap>`,
87
+ );
88
+ const result = `<?xml version="1.0" encoding="UTF-8"?>
89
+ <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
90
+ ${items.join('\n')}\n</sitemapindex>`;
91
+ resolve(result);
92
+ }
93
+ });
94
+ });
@@ -1,59 +1,25 @@
1
- import React, { useRef, useEffect } from 'react';
2
- import { useHistory, useLocation } from 'react-router-dom';
3
- import qs from 'query-string';
4
- import { useSelector } from 'react-redux';
5
- import { slugify } from '@plone/volto/helpers/Utils/Utils';
6
-
7
- /**
8
- * @function useCreatePageQueryStringKey
9
- * @description A hook that creates a key with an id if there are multiple blocks with pagination.
10
- * @returns {string} Example: page || page_012345678
11
- */
12
- const useCreatePageQueryStringKey = (id) => {
13
- const blockTypesWithPagination = ['search', 'listing'];
14
- const blocks = useSelector((state) => state?.content?.data?.blocks) || [];
15
- const blocksLayout =
16
- useSelector((state) => state?.content?.data?.blocks_layout?.items) || [];
17
- const displayedBlocks = blocksLayout?.map((item) => blocks[item]);
18
- const hasMultiplePaginations =
19
- displayedBlocks.filter((item) =>
20
- blockTypesWithPagination.includes(item['@type']),
21
- ).length > 1 || false;
22
-
23
- return hasMultiplePaginations ? slugify(`page-${id}`) : 'page';
24
- };
1
+ import React from 'react';
2
+ import { isEqual } from 'lodash';
3
+ import { usePrevious } from './usePrevious';
4
+ import useDeepCompareEffect from 'use-deep-compare-effect';
25
5
 
26
6
  /**
27
7
  * A pagination helper that tracks the query and resets pagination in case the
28
8
  * query changes.
29
9
  */
30
- export const usePagination = (id = null, defaultPage = 1) => {
31
- const location = useLocation();
32
- const history = useHistory();
33
- const pageQueryStringKey = useCreatePageQueryStringKey(id);
34
- const pageQueryParam =
35
- qs.parse(location.search)[pageQueryStringKey] || defaultPage;
36
- const [currentPage, setCurrentPage] = React.useState(
37
- parseInt(pageQueryParam),
38
- );
39
- const queryRef = useRef(qs.parse(location.search)?.query);
10
+ export const usePagination = (query, defaultPage = 1) => {
11
+ const previousQuery = usePrevious(query);
12
+ const [currentPage, setCurrentPage] = React.useState(defaultPage);
40
13
 
41
- useEffect(() => {
42
- if (queryRef.current !== qs.parse(location.search)?.query) {
43
- setCurrentPage(defaultPage);
44
- queryRef.current = qs.parse(location.search)?.query;
45
- }
46
- const newParams = {
47
- ...qs.parse(location.search),
48
- [pageQueryStringKey]: currentPage,
49
- };
50
- history.replace({
51
- search: qs.stringify(newParams),
52
- });
53
- }, [currentPage, defaultPage, location.search, history, pageQueryStringKey]);
14
+ useDeepCompareEffect(() => {
15
+ setCurrentPage(defaultPage);
16
+ }, [query, previousQuery, defaultPage]);
54
17
 
55
18
  return {
56
- currentPage,
19
+ currentPage:
20
+ previousQuery && !isEqual(previousQuery, query)
21
+ ? defaultPage
22
+ : currentPage,
57
23
  setCurrentPage,
58
24
  };
59
25
  };
@@ -1,115 +0,0 @@
1
- import { renderHook } from '@testing-library/react-hooks';
2
- import { usePagination } from './usePagination';
3
- import * as redux from 'react-redux';
4
- import routeData from 'react-router';
5
- import { slugify } from '@plone/volto/helpers/Utils/Utils';
6
-
7
- const searchBlockId = '545b33de-92cf-4747-969d-68851837b317';
8
- const searchBlockId2 = '454b33de-92cf-4747-969d-68851837b713';
9
- const searchBlock = {
10
- '@type': 'search',
11
- query: {
12
- b_size: '4',
13
- query: [
14
- {
15
- i: 'path',
16
- o: 'plone.app.querystring.operation.string.relativePath',
17
- v: '',
18
- },
19
- ],
20
- sort_order: 'ascending',
21
- },
22
- showSearchInput: true,
23
- showTotalResults: true,
24
- };
25
- let state = {
26
- content: {
27
- data: {
28
- blocks: {
29
- [searchBlockId]: searchBlock,
30
- },
31
- blocks_layout: {
32
- items: [searchBlockId],
33
- },
34
- },
35
- },
36
- };
37
-
38
- let mockUseLocationValue = {
39
- pathname: '/testroute',
40
- search: '',
41
- };
42
-
43
- const setUp = (searchParam, numberOfSearches) => {
44
- mockUseLocationValue.search = searchParam;
45
- if (numberOfSearches > 1) {
46
- state.content.data.blocks[searchBlockId2] = searchBlock;
47
- state.content.data.blocks_layout.items.push(searchBlockId2);
48
- }
49
- return renderHook(({ id, defaultPage }) => usePagination(id, defaultPage), {
50
- initialProps: {
51
- id: searchBlockId,
52
- defaultPage: 1,
53
- },
54
- });
55
- };
56
-
57
- describe(`Tests for usePagination, for the block ${searchBlockId}`, () => {
58
- const useLocation = jest.spyOn(routeData, 'useLocation');
59
- const useHistory = jest.spyOn(routeData, 'useHistory');
60
- const useSelector = jest.spyOn(redux, 'useSelector');
61
- beforeEach(() => {
62
- useLocation.mockReturnValue(mockUseLocationValue);
63
- useHistory.mockReturnValue({ replace: jest.fn() });
64
- useSelector.mockImplementation((cb) => cb(state));
65
- });
66
-
67
- it('1 paginated block with id and defaultPage 1 - shoud be 1', () => {
68
- const { result } = setUp();
69
- expect(result.current.currentPage).toBe(1);
70
- });
71
-
72
- it('1 paginated block without params - shoud be 1', () => {
73
- const { result } = setUp();
74
- expect(result.current.currentPage).toBe(1);
75
- });
76
-
77
- const param1 = '?page=2';
78
- it(`1 paginated block with params: ${param1} - shoud be 2`, () => {
79
- const { result } = setUp(param1);
80
- expect(result.current.currentPage).toBe(2);
81
- });
82
-
83
- const param2 = `?${slugify(`page-${searchBlockId}`)}=2`;
84
- it(`2 paginated blocks with current block in the params: ${param2} - shoud be 2`, () => {
85
- const { result } = setUp(param2, 2);
86
- expect(result.current.currentPage).toBe(2);
87
- });
88
-
89
- const param3 = `?${slugify(`page-${searchBlockId2}`)}=2`;
90
- it(`2 paginated blocks with the other block in the params: ${param3} - shoud be 1`, () => {
91
- const { result } = setUp(param3, 2);
92
- expect(result.current.currentPage).toBe(1);
93
- });
94
-
95
- const param4 = `?${slugify(`page-${searchBlockId}`)}=2&${slugify(
96
- `page-${searchBlockId2}`,
97
- )}=1`;
98
- it(`2 paginated blocks with both blocks in the params, current 2: ${param4} - shoud be 2`, () => {
99
- const { result } = setUp(param4, 2);
100
- expect(result.current.currentPage).toBe(2);
101
- });
102
-
103
- const param5 = `?${slugify(`page-${searchBlockId}`)}=1&${slugify(
104
- `page-${searchBlockId2}`,
105
- )}=2`;
106
- it(`2 paginated blocks with both blocks in the params, current 1: ${param5} - shoud be 1`, () => {
107
- const { result } = setUp(param5, 2);
108
- expect(result.current.currentPage).toBe(1);
109
- });
110
-
111
- it(`2 paginated blocks with wrong page param: ${param1} - shoud be 1`, () => {
112
- const { result } = setUp(param1, 2);
113
- expect(result.current.currentPage).toBe(1);
114
- });
115
- });