@plone/volto 17.13.0 → 17.14.0

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.md CHANGED
@@ -17,6 +17,19 @@ myst:
17
17
 
18
18
  <!-- towncrier release notes start -->
19
19
 
20
+ ## 17.14.0 (2024-02-18)
21
+
22
+ ### Feature
23
+
24
+ - Added the `ignore` property to allow exceptions to rules that are applied to all routes. @dobri1408 [#5621](https://github.com/plone/volto/issues/5621)
25
+
26
+ ### Bugfix
27
+
28
+ - Fixed listing SSR rendering by sending `subrequestId` instead of `id` only within `getAsyncData`, similar to calling `getQueryStringResults` directly. @ichim-david
29
+ Fixed listing SSR pagination rendering by sending `currentPage` value besides the `subrequestId`. @ichim-david
30
+ Added testing for SSR rendering for all of the listing block tests. @ichim-david [#5688](https://github.com/plone/volto/issues/5688)
31
+ - Add extra wait calls to listing block tests to avoid sporadic failures. @ichim-david [#5753](https://github.com/plone/volto/issues/5753)
32
+
20
33
  ## 17.13.0 (2024-02-09)
21
34
 
22
35
  ### Feature
package/addon-registry.js CHANGED
@@ -105,7 +105,13 @@ class AddonConfigurationRegistry {
105
105
  path.join(projectRootPath, 'package.json'),
106
106
  ));
107
107
  // Loads the dynamic config, if any
108
- if (fs.existsSync(path.join(projectRootPath, 'volto.config.js'))) {
108
+ if (process.env.VOLTOCONFIG) {
109
+ if (fs.existsSync(path.resolve(process.env.VOLTOCONFIG))) {
110
+ const voltoConfigPath = path.resolve(process.env.VOLTOCONFIG);
111
+ console.log(`Using volto.config.js in: ${voltoConfigPath}`);
112
+ this.voltoConfigJS = require(voltoConfigPath);
113
+ }
114
+ } else if (fs.existsSync(path.join(projectRootPath, 'volto.config.js'))) {
109
115
  this.voltoConfigJS = require(
110
116
  path.join(projectRootPath, 'volto.config.js'),
111
117
  );
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-console */
1
2
  import { getIfExists } from '../helpers';
2
3
  import { ploneAuth } from './constants';
3
4
 
@@ -39,6 +40,30 @@ Cypress.Commands.add('isInViewport', (element) => {
39
40
  });
40
41
  });
41
42
 
43
+ // --- isInHTML ----------------------------------------------------------
44
+ Cypress.Commands.add('isInHTML', ({ parent = 'body', content }) => {
45
+ cy.url().then((currentUrl) => {
46
+ // sometimes the cy command is called when the url is still at content/edit
47
+ // we want to query the html markup of the content, not the edit form
48
+ const url =
49
+ currentUrl.indexOf('/edit') !== -1
50
+ ? currentUrl.split('/edit')[0]
51
+ : currentUrl;
52
+ cy.request({
53
+ method: 'GET',
54
+ url: url,
55
+ }).then((response) => {
56
+ const html = Cypress.$(response.body);
57
+ if (content.startsWith('.') || content.startsWith('#')) {
58
+ return expect(html.find(parent)).to.have.descendants(content);
59
+ } else {
60
+ // check if parent contains the content text string in its HTML output
61
+ return expect(html.find(parent)).to.contain(content);
62
+ }
63
+ });
64
+ });
65
+ });
66
+
42
67
  // --- AUTOLOGIN -------------------------------------------------------------
43
68
  Cypress.Commands.add('autologin', (usr, pass) => {
44
69
  let api_url, user, password;
@@ -695,7 +720,7 @@ Cypress.Commands.add('clearSlate', (selector) => {
695
720
  return cy
696
721
  .get(selector)
697
722
  .focus()
698
- .click()
723
+ .click({ force: true }) // fix sporadic failure this element is currently animating
699
724
  .wait(1000)
700
725
  .type('{selectAll}')
701
726
  .wait(1000)
@@ -819,7 +844,6 @@ function getTextNode(el, match) {
819
844
  return walk.nextNode();
820
845
  }
821
846
 
822
- const nodes = [];
823
847
  let node;
824
848
  while ((node = walk.nextNode())) {
825
849
  if (node.wholeText.includes(match)) {
@@ -848,7 +872,7 @@ function createHtmlPasteEvent(htmlContent) {
848
872
 
849
873
  Cypress.Commands.add('addNewBlock', (blockName, createNewSlate = false) => {
850
874
  let block;
851
- block = cy.getSlate(createNewSlate).type(`/${blockName}{enter}`);
875
+ block = cy.getSlate(createNewSlate).click().type(`/${blockName}{enter}`);
852
876
  return block;
853
877
  });
854
878
 
@@ -902,9 +926,33 @@ Cypress.Commands.add('configureListingWith', (contentType) => {
902
926
  '.querystring-widget .fields:first-of-type > .field .react-select__menu .react-select__option',
903
927
  )
904
928
  .contains(contentType)
929
+
905
930
  .click();
906
931
  });
907
932
 
933
+ Cypress.Commands.add(
934
+ 'addLocationQuerystring',
935
+ (option = 'Relative path', value) => {
936
+ cy.get('.block-editor-listing').click();
937
+ cy.get('.querystring-widget .fields').contains('Add criteria').click();
938
+ cy.get('.querystring-widget .react-select__menu .react-select__option')
939
+ .contains('Location')
940
+ .click();
941
+
942
+ cy.get('.querystring-widget .fields').contains('Absolute path').click();
943
+ cy.get(
944
+ '.querystring-widget .fields .react-select__menu .react-select__option',
945
+ )
946
+ .contains(option)
947
+ .click();
948
+ if (value) {
949
+ cy.get('.querystring-widget .fields .input')
950
+ .click()
951
+ .type(`${value}{enter}`);
952
+ }
953
+ },
954
+ );
955
+
908
956
  Cypress.Commands.add('queryCounter', (path, steps, number = 1) => {
909
957
  cy.intercept(path, cy.spy().as('counterName'));
910
958
  steps.forEach((element) => {
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "17.13.0",
12
+ "version": "17.14.0",
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.13.0",
3
+ "version": "17.14.0",
4
4
  "description": "Slate.js integration with Volto",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -1,15 +1,29 @@
1
1
  import { getQueryStringResults } from '@plone/volto/actions';
2
2
  import { resolveBlockExtensions } from '@plone/volto/helpers';
3
+ import qs from 'query-string';
4
+ import { slugify } from '@plone/volto/helpers/Utils/Utils';
5
+
6
+ const getCurrentPage = (location, id) => {
7
+ const pageQueryParam = qs.parse(location.search);
8
+ switch (Object.keys(pageQueryParam).length) {
9
+ case 0:
10
+ return 1;
11
+ case 1:
12
+ // when there is only one query param, it could be the simple page number or the sluggified block id
13
+ return pageQueryParam['page'] || pageQueryParam[slugify(`page-${id}`)];
14
+ default:
15
+ return pageQueryParam[slugify(`page-${id}`)];
16
+ }
17
+ };
18
+
19
+ export default function getListingBlockAsyncData(props) {
20
+ const { data, path, location, id, dispatch, blocksConfig, content } = props;
3
21
 
4
- export default function getListingBlockAsyncData({
5
- dispatch,
6
- id,
7
- data,
8
- path,
9
- blocksConfig,
10
- }) {
11
22
  const { resolvedExtensions } = resolveBlockExtensions(data, blocksConfig);
12
23
 
24
+ const subrequestID = content?.UID ? `${content?.UID}-${id}` : id;
25
+ const currentPage = getCurrentPage(location, id);
26
+
13
27
  return [
14
28
  dispatch(
15
29
  getQueryStringResults(
@@ -20,7 +34,8 @@ export default function getListingBlockAsyncData({
20
34
  ? { fullobjects: 1 }
21
35
  : { metadata_fields: '_all' }),
22
36
  },
23
- id,
37
+ subrequestID,
38
+ currentPage,
24
39
  ),
25
40
  ),
26
41
  ];
@@ -242,6 +242,7 @@ export const fetchContent = async ({ store, location }) => {
242
242
  id,
243
243
  data,
244
244
  blocksConfig,
245
+ content,
245
246
  });
246
247
  if (!p?.length) {
247
248
  throw new Error(
@@ -8,6 +8,8 @@ const AppExtras = (props) => {
8
8
  const { pathname } = props;
9
9
  const active = appExtras
10
10
  .map((reg) => {
11
+ const ignored = matchPath(pathname, reg.ignore);
12
+ if (ignored) return null;
11
13
  const match = matchPath(pathname, reg.match);
12
14
  return match ? { reg, match } : null;
13
15
  })
@@ -46,6 +46,13 @@ beforeAll(() => {
46
46
  <div className="something">{JSON.stringify(props.match)}</div>
47
47
  )),
48
48
  },
49
+ {
50
+ match: '/frontpage',
51
+ ignore: '/frontpage/images',
52
+ component: jest.fn((props) => (
53
+ <div className="frontpage-content">{JSON.stringify(props.match)}</div>
54
+ )),
55
+ },
49
56
  ];
50
57
  });
51
58
 
@@ -85,4 +92,17 @@ describe('AppExtras', () => {
85
92
  const json = component.toJSON();
86
93
  expect(json).toMatchSnapshot();
87
94
  });
95
+ it('ignore property works', () => {
96
+ const componentView = renderer.create(
97
+ <AppExtras pathname="/frontpage"></AppExtras>,
98
+ );
99
+ const componentEdit = renderer.create(
100
+ <AppExtras pathname="/frontpage/images"></AppExtras>,
101
+ );
102
+
103
+ const jsonView = componentView.toJSON();
104
+ expect(jsonView).toMatchSnapshot();
105
+ const jsonEdit = componentEdit.toJSON();
106
+ expect(jsonEdit).toMatchSnapshot();
107
+ });
88
108
  });
@@ -1,7 +1 @@
1
- export default function getListingBlockAsyncData({ dispatch, id, data, path, blocksConfig, }: {
2
- dispatch: any;
3
- id: any;
4
- data: any;
5
- path: any;
6
- blocksConfig: any;
7
- }): any[];
1
+ export default function getListingBlockAsyncData(props: any): any[];