@eeacms/volto-group-block 9.0.0 → 10.0.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/.eslintrc.js CHANGED
@@ -3,6 +3,11 @@ const path = require('path');
3
3
  const projectRootPath = fs.realpathSync(__dirname + '/../../../');
4
4
 
5
5
  let voltoPath = path.join(projectRootPath, 'node_modules/@plone/volto');
6
+ const voltoSlatePath = fs.existsSync(
7
+ path.join(projectRootPath, 'node_modules/@plone/volto-slate/src'),
8
+ )
9
+ ? '@plone/volto-slate/src'
10
+ : '@plone/volto/packages/volto-slate/src';
6
11
  let configFile;
7
12
  if (fs.existsSync(`${projectRootPath}/tsconfig.json`))
8
13
  configFile = `${projectRootPath}/tsconfig.json`;
@@ -34,7 +39,7 @@ const defaultConfig = {
34
39
  alias: {
35
40
  map: [
36
41
  ['@plone/volto', '@plone/volto/src'],
37
- ['@plone/volto-slate', '@plone/volto/packages/volto-slate/src'],
42
+ ['@plone/volto-slate', voltoSlatePath],
38
43
  ...addonAliases,
39
44
  ['@package', `${__dirname}/src`],
40
45
  ['@root', `${__dirname}/src`],
@@ -54,7 +59,7 @@ const defaultConfig = {
54
59
  allowReferrer: true,
55
60
  },
56
61
  ],
57
- }
62
+ },
58
63
  };
59
64
 
60
65
  const config = addonExtenders.reduce(
package/CHANGELOG.md CHANGED
@@ -4,7 +4,19 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
- ### [9.0.0](https://github.com/eea/volto-group-block/compare/8.1.1...9.0.0) - 10 February 2026
7
+ ### [10.0.0](https://github.com/eea/volto-group-block/compare/9.0.0...10.0.0) - 27 March 2026
8
+
9
+ #### :rocket: New Features
10
+
11
+ - feat: Volto 18 support - refs #287700 [Nilesh - [`eab0346`](https://github.com/eea/volto-group-block/commit/eab0346ec36eedc552958b744ae82927b88058cf)]
12
+
13
+ #### :house: Internal changes
14
+
15
+
16
+ #### :hammer_and_wrench: Others
17
+
18
+ - tests: Fix Sonar Qube tags - refs #297339 [Alin Voinea - [`75babfe`](https://github.com/eea/volto-group-block/commit/75babfeaa1ee5f707803a24a532a28c969d94a19)]
19
+ ## [9.0.0](https://github.com/eea/volto-group-block/compare/8.1.1...9.0.0) - 10 February 2026
8
20
 
9
21
  #### :rocket: New Features
10
22
 
package/DEVELOP.md CHANGED
@@ -12,6 +12,8 @@
12
12
  make start
13
13
  ```
14
14
 
15
+ `make start` defaults to Volto 18. Use `make VOLTO_VERSION=17 start` to test the Volto 17 dev environment.
16
+
15
17
  1. Wait for `Volto started at 0.0.0.0:3000` meesage
16
18
 
17
19
  1. Go to http://localhost:3000
@@ -26,21 +28,20 @@
26
28
 
27
29
  ### Or add @eeacms/volto-group-block to your Volto project
28
30
 
29
- Before starting make sure your development environment is properly set. See [Volto Developer Documentation](https://docs.voltocms.com/getting-started/install/)
30
-
31
- 1. Make sure you have installed `yo`, `@plone/generator-volto` and `mrs-developer`
31
+ Before starting make sure your development environment is properly set. See the official Plone documentation for [creating a project with Cookieplone](https://6.docs.plone.org/install/create-project-cookieplone.html) and [installing an add-on in development mode in Volto 18 and 19](https://6.docs.plone.org/volto/development/add-ons/install-an-add-on-dev-18.html).
32
32
 
33
- npm install -g yo @plone/generator-volto mrs-developer
33
+ For new Volto 18+ projects, use Cookieplone. It includes `mrs-developer` by default.
34
34
 
35
- 1. Create new volto app
35
+ 1. Create a new Volto project with Cookieplone
36
36
 
37
- yo @plone/volto my-volto-project --addon @eeacms/volto-group-block --skip-install
38
- cd my-volto-project
37
+ uvx cookieplone project
38
+ cd project-title
39
39
 
40
40
  1. Add the following to `mrs.developer.json`:
41
41
 
42
42
  {
43
43
  "volto-group-block": {
44
+ "output": "packages",
44
45
  "url": "https://github.com/eea/volto-group-block.git",
45
46
  "package": "@eeacms/volto-group-block",
46
47
  "branch": "develop",
@@ -48,28 +49,31 @@ Before starting make sure your development environment is properly set. See [Vol
48
49
  }
49
50
  }
50
51
 
51
- 1. Install
52
+ 1. Add `@eeacms/volto-group-block` to the `addons` key in your project `volto.config.js`
53
+
54
+ 1. Install or refresh the project setup
52
55
 
53
- make develop
54
- yarn
56
+ make install
55
57
 
56
- 1. Start backend
58
+ 1. Start backend in one terminal
57
59
 
58
- docker run --pull always -it --rm --name plone -p 8080:8080 -e SITE=Plone plone/plone-backend
60
+ make backend-start
59
61
 
60
- ...wait for backend to setup and start - `Ready to handle requests`:
62
+ ...wait for backend to setup and start, ending with `Ready to handle requests`
61
63
 
62
64
  ...you can also check http://localhost:8080/Plone
63
65
 
64
- 1. Start frontend
66
+ 1. Start frontend in a second terminal
65
67
 
66
- yarn start
68
+ make frontend-start
67
69
 
68
70
  1. Go to http://localhost:3000
69
71
 
70
72
  1. Happy hacking!
71
73
 
72
- cd src/addons/volto-group-block/
74
+ cd packages/volto-group-block
75
+
76
+ For legacy Volto 17 projects, keep using the yarn-based workflow from the Volto 17 documentation.
73
77
 
74
78
  ## Cypress
75
79
 
@@ -81,7 +85,7 @@ project where you added `volto-group-block` to `mrs.developer.json`
81
85
  Go to:
82
86
 
83
87
  ```BASH
84
- cd src/addons/volto-group-block/
88
+ cd packages/volto-group-block/
85
89
  ```
86
90
 
87
91
  Start:
@@ -93,6 +97,8 @@ Start:
93
97
 
94
98
  This will build and start with Docker a clean `Plone backend` and `Volto Frontend` with `volto-group-block` block installed.
95
99
 
100
+ Use `make VOLTO_VERSION=17 start` if you need to reproduce the Volto 17 setup locally.
101
+
96
102
  Open Cypress Interface:
97
103
 
98
104
  ```Bash
package/README.md CHANGED
@@ -3,16 +3,16 @@
3
3
  [![Releases](https://img.shields.io/github/v/release/eea/volto-group-block)](https://github.com/eea/volto-group-block/releases)
4
4
 
5
5
  [![Pipeline](https://ci.eionet.europa.eu/buildStatus/icon?job=volto-addons%2Fvolto-group-block%2Fmaster&subject=master)](https://ci.eionet.europa.eu/view/Github/job/volto-addons/job/volto-group-block/job/master/display/redirect)
6
- [![Lines of Code](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block-master&metric=ncloc)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block-master)
7
- [![Coverage](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block-master&metric=coverage)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block-master)
8
- [![Bugs](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block-master&metric=bugs)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block-master)
9
- [![Duplicated Lines (%)](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block-master&metric=duplicated_lines_density)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block-master)
6
+ [![Lines of Code](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block&metric=ncloc)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block)
7
+ [![Coverage](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block&metric=coverage)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block)
8
+ [![Bugs](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block&metric=bugs)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block)
9
+ [![Duplicated Lines (%)](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block&metric=duplicated_lines_density)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block)
10
10
 
11
11
  [![Pipeline](https://ci.eionet.europa.eu/buildStatus/icon?job=volto-addons%2Fvolto-group-block%2Fdevelop&subject=develop)](https://ci.eionet.europa.eu/view/Github/job/volto-addons/job/volto-group-block/job/develop/display/redirect)
12
- [![Lines of Code](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block-develop&metric=ncloc)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block-develop)
13
- [![Coverage](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block-develop&metric=coverage)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block-develop)
14
- [![Bugs](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block-develop&metric=bugs)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block-develop)
15
- [![Duplicated Lines (%)](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block-develop&metric=duplicated_lines_density)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block-develop)
12
+ [![Lines of Code](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block&branch=develop&metric=ncloc)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block&branch=develop)
13
+ [![Coverage](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block&branch=develop&metric=coverage)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block&branch=develop)
14
+ [![Bugs](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block&branch=develop&metric=bugs)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block&branch=develop)
15
+ [![Duplicated Lines (%)](https://sonarqube.eea.europa.eu/api/project_badges/measure?project=volto-group-block&branch=develop&metric=duplicated_lines_density)](https://sonarqube.eea.europa.eu/dashboard?id=volto-group-block&branch=develop)
16
16
 
17
17
  [Volto](https://github.com/plone/volto) add-on to group blocks in sections and filter available blocks per content-type per section
18
18
 
@@ -20,6 +20,18 @@
20
20
 
21
21
  ![Group blocks and restrict available blocks](https://raw.githubusercontent.com/eea/volto-group-block/master/docs/volto-group-block.gif)
22
22
 
23
+ ## Upgrade
24
+
25
+ ### Upgrading to 10.x
26
+
27
+ > This version requires `Volto >= 17.18` or `Volto 18+`. It removes the custom `EditBlockWrapper` and uses Volto's built-in block chrome provided by `BlocksForm`.
28
+
29
+ #### Breaking changes
30
+
31
+ - **Removed `EditBlockWrapper.jsx`.** Any code importing `EditBlockWrapper` from `@eeacms/volto-group-block` will break. Use Volto's built-in `EditBlockWrapper` from `@plone/volto/components/manage/Blocks/Block/EditBlockWrapper` if you need a custom wrapper.
32
+ - **Removed the "Section help" button.** The `?` icon that appeared in the inner block toolbar and opened the sidebar to display editing instructions has been removed. The `instructions` field is still available in the group block schema and still rendered in the sidebar panel, but there is no longer a per-block shortcut button to reveal it.
33
+ - **`disableInnerButtons`** is now implemented via a CSS class (`.disable-inner-buttons`) instead of a JS-level `disabled` prop on the removed wrapper.
34
+
23
35
  ## Getting started
24
36
 
25
37
  ### Try volto-group-block with Docker
@@ -31,6 +43,11 @@
31
43
 
32
44
  Go to http://localhost:3000
33
45
 
46
+ `make start` now defaults to Volto 18. To run the same setup against Volto 17, use:
47
+
48
+ VOLTO_VERSION=17 make
49
+ VOLTO_VERSION=17 make start
50
+
34
51
  ### Add volto-group-block to your Volto project
35
52
 
36
53
  1. Make sure you have a [Plone backend](https://plone.org/download) up-and-running at http://localhost:8080/Plone
@@ -44,30 +61,39 @@ Go to http://localhost:3000
44
61
  * If you already have a volto project, just update `package.json`:
45
62
 
46
63
  ```JSON
47
- "addons": [
48
- "@eeacms/volto-group-block"
49
- ],
50
-
51
64
  "dependencies": {
52
65
  "@eeacms/volto-group-block": "*"
53
66
  }
54
67
  ```
55
68
 
56
- * If not, create one:
69
+ and `volto.config.js`:
57
70
 
71
+ ```JavaScript
72
+ const addons = ['@eeacms/volto-group-block'];
58
73
  ```
59
- npm install -g yo @plone/generator-volto
60
- yo @plone/volto my-volto-project --canary --addon @eeacms/volto-group-block
61
- cd my-volto-project
74
+
75
+ * If not, create one with Cookieplone, as recommended by the official Plone documentation for Volto 18+:
76
+
77
+ ```
78
+ uvx cookieplone project
79
+ cd project-title
80
+ ```
81
+
82
+ 1. Install or update dependencies, then start the project:
83
+
84
+ ```
85
+ make install
62
86
  ```
63
87
 
64
- 1. Install new add-ons and restart Volto:
88
+ For a Cookieplone project, start the backend and frontend in separate terminals:
65
89
 
66
90
  ```
67
- yarn
68
- yarn start
91
+ make backend-start
92
+ make frontend-start
69
93
  ```
70
94
 
95
+ For a legacy Volto 17 project, install the package with `yarn` and restart the frontend as usual.
96
+
71
97
  1. Go to http://localhost:3000
72
98
 
73
99
  1. Happy editing!
package/cypress.config.js CHANGED
@@ -1,9 +1,18 @@
1
1
  const { defineConfig } = require('cypress');
2
2
 
3
+ const apiPath =
4
+ process.env.CYPRESS_API_PATH ||
5
+ process.env.RAZZLE_DEV_PROXY_API_PATH ||
6
+ process.env.RAZZLE_INTERNAL_API_PATH ||
7
+ 'http://localhost:8080/Plone';
8
+
3
9
  module.exports = defineConfig({
4
10
  viewportWidth: 1280,
5
11
  defaultCommandTimeout: 8888,
6
12
  chromeWebSecurity: false,
13
+ env: {
14
+ API_PATH: apiPath,
15
+ },
7
16
  reporter: 'junit',
8
17
  video: false,
9
18
  retries: {
@@ -15,7 +15,7 @@ services:
15
15
  args:
16
16
  ADDON_NAME: "${ADDON_NAME}"
17
17
  ADDON_PATH: "${ADDON_PATH}"
18
- VOLTO_VERSION: ${VOLTO_VERSION:-16}
18
+ VOLTO_VERSION: ${VOLTO_VERSION:-18-yarn}
19
19
  ports:
20
20
  - "3000:3000"
21
21
  - "3001:3001"
@@ -1,4 +1,12 @@
1
1
  require('dotenv').config({ path: __dirname + '/.env' })
2
+ const fs = require('fs')
3
+ const path = require('path')
4
+
5
+ const voltoSlatePath = fs.existsSync(
6
+ path.join(__dirname, '../../../node_modules/@plone/volto-slate/src'),
7
+ )
8
+ ? '<rootDir>/node_modules/@plone/volto-slate/src'
9
+ : '<rootDir>/node_modules/@plone/volto/packages/volto-slate/src'
2
10
 
3
11
  module.exports = {
4
12
  testMatch: ['**/src/addons/**/?(*.)+(spec|test).[jt]s?(x)'],
@@ -17,10 +25,8 @@ module.exports = {
17
25
  '@eeacms/search/(.*)$': '<rootDir>/src/addons/volto-searchlib/searchlib/$1',
18
26
  '@eeacms/search': '<rootDir>/src/addons/volto-searchlib/searchlib',
19
27
  '@eeacms/(.*?)/(.*)$': '<rootDir>/node_modules/@eeacms/$1/src/$2',
20
- '@plone/volto-slate$':
21
- '<rootDir>/node_modules/@plone/volto/packages/volto-slate/src',
22
- '@plone/volto-slate/(.*)$':
23
- '<rootDir>/node_modules/@plone/volto/packages/volto-slate/src/$1',
28
+ '@plone/volto-slate$': voltoSlatePath,
29
+ '@plone/volto-slate/(.*)$': `${voltoSlatePath}/$1`,
24
30
  '~/(.*)$': '<rootDir>/src/$1',
25
31
  'load-volto-addons':
26
32
  '<rootDir>/node_modules/@plone/volto/jest-addons-loader.js',
@@ -21,16 +21,6 @@ msgstr "Abschnitt (Gruppe)"
21
21
  msgid "SectionGroupSettings"
22
22
  msgstr "Abschnitt (Gruppe) Einstellungen"
23
23
 
24
- #. Default: "Unknown Block {block}"
25
- #: components/manage/Blocks/Group/EditBlockWrapper
26
- msgid "Unknown Block"
27
- msgstr "Unbekannter Block {block}"
28
-
29
- #. Default: "Add block"
30
- #: components/manage/Blocks/Group/EditBlockWrapper
31
- msgid "addBlock"
32
- msgstr "Block hinzufügen"
33
-
34
24
  #. Default: "Select HTML5 element to be used for this block"
35
25
  #: components/manage/Blocks/Group/EditSchema
36
26
  msgid "asPropertyDescription"
@@ -41,21 +31,11 @@ msgstr "Wählen Sie das HTML5-Element, das für diesen Block verwendet werden so
41
31
  msgid "asPropertyTitle"
42
32
  msgstr ""
43
33
 
44
- #. Default: "delete"
45
- #: components/manage/Blocks/Group/EditBlockWrapper
46
- msgid "delete"
47
- msgstr "Löschen"
48
-
49
34
  #. Default: "Default"
50
35
  #: components/manage/Blocks/Group/EditSchema
51
36
  msgid "fieldsetDefaultTitle"
52
37
  msgstr "Standard"
53
38
 
54
- #. Default: "Remove block"
55
- #: components/manage/Blocks/Group/EditBlockWrapper
56
- msgid "removeBlock"
57
- msgstr "Block entfernen"
58
-
59
39
  #. Default: "Section block"
60
40
  #: components/manage/Blocks/Group/EditSchema
61
41
  msgid "sectionBlock"
@@ -21,16 +21,6 @@ msgstr ""
21
21
  msgid "SectionGroupSettings"
22
22
  msgstr ""
23
23
 
24
- #. Default: "Unknown Block {block}"
25
- #: components/manage/Blocks/Group/EditBlockWrapper
26
- msgid "Unknown Block"
27
- msgstr ""
28
-
29
- #. Default: "Add block"
30
- #: components/manage/Blocks/Group/EditBlockWrapper
31
- msgid "addBlock"
32
- msgstr ""
33
-
34
24
  #. Default: "Select HTML5 element to be used for this block"
35
25
  #: components/manage/Blocks/Group/EditSchema
36
26
  msgid "asPropertyDescription"
@@ -41,21 +31,11 @@ msgstr ""
41
31
  msgid "asPropertyTitle"
42
32
  msgstr ""
43
33
 
44
- #. Default: "delete"
45
- #: components/manage/Blocks/Group/EditBlockWrapper
46
- msgid "delete"
47
- msgstr ""
48
-
49
34
  #. Default: "Default"
50
35
  #: components/manage/Blocks/Group/EditSchema
51
36
  msgid "fieldsetDefaultTitle"
52
37
  msgstr ""
53
38
 
54
- #. Default: "Remove block"
55
- #: components/manage/Blocks/Group/EditBlockWrapper
56
- msgid "removeBlock"
57
- msgstr ""
58
-
59
39
  #. Default: "Section block"
60
40
  #: components/manage/Blocks/Group/EditSchema
61
41
  msgid "sectionBlock"
@@ -21,16 +21,6 @@ msgstr ""
21
21
  msgid "SectionGroupSettings"
22
22
  msgstr ""
23
23
 
24
- #. Default: "Unknown Block {block}"
25
- #: components/manage/Blocks/Group/EditBlockWrapper
26
- msgid "Unknown Block"
27
- msgstr ""
28
-
29
- #. Default: "Add block"
30
- #: components/manage/Blocks/Group/EditBlockWrapper
31
- msgid "addBlock"
32
- msgstr ""
33
-
34
24
  #. Default: "Select HTML5 element to be used for this block"
35
25
  #: components/manage/Blocks/Group/EditSchema
36
26
  msgid "asPropertyDescription"
@@ -41,21 +31,11 @@ msgstr ""
41
31
  msgid "asPropertyTitle"
42
32
  msgstr ""
43
33
 
44
- #. Default: "delete"
45
- #: components/manage/Blocks/Group/EditBlockWrapper
46
- msgid "delete"
47
- msgstr ""
48
-
49
34
  #. Default: "Default"
50
35
  #: components/manage/Blocks/Group/EditSchema
51
36
  msgid "fieldsetDefaultTitle"
52
37
  msgstr ""
53
38
 
54
- #. Default: "Remove block"
55
- #: components/manage/Blocks/Group/EditBlockWrapper
56
- msgid "removeBlock"
57
- msgstr ""
58
-
59
39
  #. Default: "Section block"
60
40
  #: components/manage/Blocks/Group/EditSchema
61
41
  msgid "sectionBlock"
@@ -21,16 +21,6 @@ msgstr ""
21
21
  msgid "SectionGroupSettings"
22
22
  msgstr ""
23
23
 
24
- #. Default: "Unknown Block {block}"
25
- #: components/manage/Blocks/Group/EditBlockWrapper
26
- msgid "Unknown Block"
27
- msgstr ""
28
-
29
- #. Default: "Add block"
30
- #: components/manage/Blocks/Group/EditBlockWrapper
31
- msgid "addBlock"
32
- msgstr ""
33
-
34
24
  #. Default: "Select HTML5 element to be used for this block"
35
25
  #: components/manage/Blocks/Group/EditSchema
36
26
  msgid "asPropertyDescription"
@@ -41,21 +31,11 @@ msgstr ""
41
31
  msgid "asPropertyTitle"
42
32
  msgstr ""
43
33
 
44
- #. Default: "delete"
45
- #: components/manage/Blocks/Group/EditBlockWrapper
46
- msgid "delete"
47
- msgstr ""
48
-
49
34
  #. Default: "Default"
50
35
  #: components/manage/Blocks/Group/EditSchema
51
36
  msgid "fieldsetDefaultTitle"
52
37
  msgstr ""
53
38
 
54
- #. Default: "Remove block"
55
- #: components/manage/Blocks/Group/EditBlockWrapper
56
- msgid "removeBlock"
57
- msgstr ""
58
-
59
39
  #. Default: "Section block"
60
40
  #: components/manage/Blocks/Group/EditSchema
61
41
  msgid "sectionBlock"
package/locales/volto.pot CHANGED
@@ -23,16 +23,6 @@ msgstr ""
23
23
  msgid "SectionGroupSettings"
24
24
  msgstr ""
25
25
 
26
- #. Default: "Unknown Block {block}"
27
- #: components/manage/Blocks/Group/EditBlockWrapper
28
- msgid "Unknown Block"
29
- msgstr ""
30
-
31
- #. Default: "Add block"
32
- #: components/manage/Blocks/Group/EditBlockWrapper
33
- msgid "addBlock"
34
- msgstr ""
35
-
36
26
  #. Default: "Select HTML5 element to be used for this block"
37
27
  #: components/manage/Blocks/Group/EditSchema
38
28
  msgid "asPropertyDescription"
@@ -43,21 +33,11 @@ msgstr ""
43
33
  msgid "asPropertyTitle"
44
34
  msgstr ""
45
35
 
46
- #. Default: "delete"
47
- #: components/manage/Blocks/Group/EditBlockWrapper
48
- msgid "delete"
49
- msgstr ""
50
-
51
36
  #. Default: "Default"
52
37
  #: components/manage/Blocks/Group/EditSchema
53
38
  msgid "fieldsetDefaultTitle"
54
39
  msgstr ""
55
40
 
56
- #. Default: "Remove block"
57
- #: components/manage/Blocks/Group/EditBlockWrapper
58
- msgid "removeBlock"
59
- msgstr ""
60
-
61
41
  #. Default: "Section block"
62
42
  #: components/manage/Blocks/Group/EditSchema
63
43
  msgid "sectionBlock"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-group-block",
3
- "version": "9.0.0",
3
+ "version": "10.0.0",
4
4
  "description": "volto-group-block: Volto block to be used to group other blocks",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { render } from '@testing-library/react';
3
3
  import CounterComponent from './CounterComponent';
4
- import '@testing-library/jest-dom/extend-expect';
4
+ import '@testing-library/jest-dom';
5
5
 
6
6
  jest.mock('@plone/volto/registry', () => ({
7
7
  blocks: {
@@ -1,11 +1,7 @@
1
- import { Button } from 'semantic-ui-react';
2
- import { BlocksForm, Icon, RenderBlocks } from '@plone/volto/components';
3
- import EditBlockWrapper from './EditBlockWrapper';
1
+ import { BlocksForm, RenderBlocks } from '@plone/volto/components';
4
2
  import { countTextInBlocks } from './CounterComponent';
5
3
  import { useLocation } from 'react-router-dom';
6
4
 
7
- import helpSVG from '@plone/volto/icons/help.svg';
8
-
9
5
  const GroupBlockDefaultBody = (props) => {
10
6
  const location = useLocation();
11
7
  const {
@@ -17,10 +13,8 @@ const GroupBlockDefaultBody = (props) => {
17
13
  selected,
18
14
  selectedBlock,
19
15
  onSelectBlock,
20
- setSelectedBlock,
21
16
  manage,
22
17
  childBlocksForm,
23
- multiSelected,
24
18
  formDescription,
25
19
  isEditMode,
26
20
  } = props;
@@ -42,6 +36,8 @@ const GroupBlockDefaultBody = (props) => {
42
36
  allowedBlocks={data.allowedBlocks}
43
37
  title={data.placeholder}
44
38
  description={instructions}
39
+ isMainForm={false}
40
+ stopPropagation={selectedBlock}
45
41
  onSelectBlock={(id, l, e) => {
46
42
  const isMultipleSelection = e
47
43
  ? e.shiftKey || e.ctrlKey || e.metaKey
@@ -86,38 +82,7 @@ const GroupBlockDefaultBody = (props) => {
86
82
  }
87
83
  }}
88
84
  pathname={pathname}
89
- >
90
- {({ draginfo }, editBlock, blockProps) => (
91
- <EditBlockWrapper
92
- draginfo={draginfo}
93
- blockProps={blockProps}
94
- disabled={data.disableInnerButtons}
95
- extraControls={
96
- <>
97
- {instructions && (
98
- <>
99
- <Button
100
- icon
101
- basic
102
- title="Section help"
103
- onClick={() => {
104
- setSelectedBlock();
105
- const tab = manage ? 0 : 1;
106
- props.setSidebarTab(tab);
107
- }}
108
- >
109
- <Icon name={helpSVG} className="" size="19px" />
110
- </Button>
111
- </>
112
- )}
113
- </>
114
- }
115
- multiSelected={multiSelected.includes(blockProps.block)}
116
- >
117
- {editBlock}
118
- </EditBlockWrapper>
119
- )}
120
- </BlocksForm>
85
+ />
121
86
  ) : (
122
87
  <RenderBlocks
123
88
  location={location}
@@ -3,7 +3,9 @@ import { render } from '@testing-library/react';
3
3
  import { Provider } from 'react-intl-redux';
4
4
  import DefaultBody from './DefaultBody';
5
5
  import configureStore from 'redux-mock-store';
6
- import '@testing-library/jest-dom/extend-expect';
6
+ import '@testing-library/jest-dom';
7
+
8
+ const mockBlocksForm = jest.fn();
7
9
 
8
10
  jest.mock('react-router-dom', () => ({
9
11
  ...jest.requireActual('react-router-dom'),
@@ -15,7 +17,10 @@ jest.mock('react-router-dom', () => ({
15
17
  }));
16
18
 
17
19
  jest.mock('@plone/volto/components', () => ({
18
- BlocksForm: jest.fn(() => <div className="blocks-form">RenderBlocks</div>),
20
+ BlocksForm: jest.fn((props) => {
21
+ mockBlocksForm(props);
22
+ return <div className="blocks-form">RenderBlocks</div>;
23
+ }),
19
24
  RenderBlocks: jest.fn(() => <div>RenderBlocks</div>),
20
25
  }));
21
26
 
@@ -28,6 +33,10 @@ const store = mockStore({
28
33
  });
29
34
 
30
35
  describe('DefaultBody', () => {
36
+ beforeEach(() => {
37
+ mockBlocksForm.mockClear();
38
+ });
39
+
31
40
  it('renders children', () => {
32
41
  const props = {
33
42
  data: {
@@ -55,14 +64,26 @@ describe('DefaultBody Edit', () => {
55
64
  variation: {},
56
65
  allowedBlocks: ['listing'],
57
66
  },
67
+ childBlocksForm: {
68
+ blocks: {
69
+ a: {
70
+ '@type': 'slate',
71
+ },
72
+ },
73
+ blocks_layout: {
74
+ items: ['a'],
75
+ },
76
+ },
58
77
  metadata: {},
59
78
  properties: {},
60
79
  variation: {},
61
80
  onSelectBlock: jest.fn(),
62
- onDeleteBlock: jest.fn(),
63
- onMutateBlock: jest.fn(),
64
- onInsertBlock: jest.fn(),
81
+ onChangeBlock: jest.fn(),
82
+ onChangeField: jest.fn(),
83
+ selectedBlock: 'a',
65
84
  selected: true,
85
+ manage: true,
86
+ pathname: '/',
66
87
  };
67
88
 
68
89
  const { getByText } = render(
@@ -71,5 +92,7 @@ describe('DefaultBody Edit', () => {
71
92
  </Provider>,
72
93
  );
73
94
  expect(getByText('RenderBlocks')).toBeInTheDocument();
95
+ expect(mockBlocksForm).toHaveBeenCalledTimes(1);
96
+ expect(mockBlocksForm.mock.calls[0][0].children).toBeUndefined();
74
97
  });
75
98
  });
@@ -167,7 +167,9 @@ const Edit = (props) => {
167
167
  <fieldset
168
168
  role="presentation"
169
169
  id={props.data.id}
170
- className={cx('section-block', props.data.className)}
170
+ className={cx('section-block', props.data.className, {
171
+ 'disable-inner-buttons': data.disableInnerButtons,
172
+ })}
171
173
  onKeyDown={(e) => {
172
174
  handleKeyDown(e, props.index, props.block, props.blockNode.current);
173
175
  }}
@@ -4,7 +4,7 @@ import configureStore from 'redux-mock-store';
4
4
  import { Provider } from 'react-intl-redux';
5
5
  import thunk from 'redux-thunk';
6
6
  import { render, screen, fireEvent } from '@testing-library/react';
7
- import '@testing-library/jest-dom/extend-expect';
7
+ import '@testing-library/jest-dom';
8
8
 
9
9
  const mockStore = configureStore([thunk]);
10
10
  const store = mockStore({
@@ -90,6 +90,22 @@ describe('Edit', () => {
90
90
  expect(getByRole('presentation')).toBeInTheDocument();
91
91
  });
92
92
 
93
+ it('adds a root modifier when inner buttons are disabled', () => {
94
+ const { getByRole } = render(
95
+ <Provider store={store}>
96
+ <Edit
97
+ {...props}
98
+ data={{
99
+ ...props.data,
100
+ disableInnerButtons: true,
101
+ }}
102
+ />
103
+ </Provider>,
104
+ );
105
+
106
+ expect(getByRole('presentation')).toHaveClass('disable-inner-buttons');
107
+ });
108
+
93
109
  it('should call ArrowUp keydown', () => {
94
110
  const mockOnFocusPreviousBlock = jest.fn();
95
111
  const { getByRole } = render(
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import View from './View';
3
3
  import { render, screen } from '@testing-library/react';
4
4
  import { RenderBlocks } from '@plone/volto/components';
5
- import '@testing-library/jest-dom/extend-expect';
5
+ import '@testing-library/jest-dom';
6
6
 
7
7
  jest.mock('@plone/volto/components', () => ({
8
8
  RenderBlocks: jest.fn(() => <div>RenderBlocks</div>),
@@ -10,10 +10,6 @@
10
10
  margin-bottom: 1rem;
11
11
  }
12
12
 
13
- .block-add-button {
14
- display: none !important;
15
- }
16
-
17
13
  .block.group.selected::before,
18
14
  .block.group:hover::before {
19
15
  border-style: dashed;
@@ -70,32 +66,11 @@
70
66
  margin-top: 0.5rem;
71
67
  }
72
68
 
73
- .blocks-chooser {
74
- position: absolute;
75
- right: 0 !important;
76
- left: auto !important;
77
- margin-top: 3rem;
78
- }
79
-
80
- .block-toolbar {
81
- position: absolute;
82
- z-index: 3;
83
- right: -9px;
84
- display: flex;
85
- border: none;
86
- border: 1px solid @borderColor;
87
- border-bottom: 1px solid @pageBackground;
88
- margin-top: -45px;
89
- background-color: @pageBackground;
90
- border-top-left-radius: 1rem;
91
- border-top-right-radius: 1rem;
92
-
93
- .ui.basic.button {
94
- padding: 8px 5px;
95
- }
96
-
97
- .ui.basic.button:hover {
98
- background: transparent !important;
69
+ .section-block.disable-inner-buttons {
70
+ .drag.handle.wrapper,
71
+ .delete-button,
72
+ .block-add-button {
73
+ display: none !important;
99
74
  }
100
75
  }
101
76
  }
@@ -1,197 +0,0 @@
1
- import React from 'react';
2
- import { Icon, BlockChooser } from '@plone/volto/components';
3
- import {
4
- blockHasValue,
5
- buildStyleClassNamesFromData,
6
- } from '@plone/volto/helpers';
7
- import { Button } from 'semantic-ui-react';
8
- import includes from 'lodash/includes';
9
- import isBoolean from 'lodash/isBoolean';
10
- import { defineMessages, injectIntl } from 'react-intl';
11
- import cx from 'classnames';
12
- import config from '@plone/volto/registry';
13
- import { doesNodeContainClick } from 'semantic-ui-react/dist/commonjs/lib';
14
-
15
- import dragSVG from '@plone/volto/icons/drag.svg';
16
- import addSVG from '@plone/volto/icons/circle-plus.svg';
17
- import trashSVG from '@plone/volto/icons/delete.svg';
18
-
19
- const messages = defineMessages({
20
- unknownBlock: {
21
- id: 'Unknown Block',
22
- defaultMessage: 'Unknown Block {block}',
23
- },
24
- delete: {
25
- id: 'delete',
26
- defaultMessage: 'delete',
27
- },
28
- addBlock: {
29
- id: 'addBlock',
30
- defaultMessage: 'Add block',
31
- },
32
- removeBlock: {
33
- id: 'removeBlock',
34
- defaultMessage: 'Remove block',
35
- },
36
- });
37
-
38
- class EditBlockWrapper extends React.Component {
39
- constructor(props) {
40
- super(props);
41
- this.state = {
42
- addNewBlockOpened: false,
43
- };
44
- }
45
-
46
- componentDidMount() {
47
- document.addEventListener('mousedown', this.handleClickOutside, false);
48
- }
49
-
50
- componentWillUnmount() {
51
- document.removeEventListener('mousedown', this.handleClickOutside);
52
- }
53
-
54
- handleClickOutside = (e) => {
55
- if (
56
- this.blockNode.current &&
57
- doesNodeContainClick(this.blockNode.current, e)
58
- )
59
- return;
60
-
61
- if (this.state.addNewBlockOpened) {
62
- this.setState({
63
- addNewBlockOpened: false,
64
- });
65
- return true;
66
- }
67
- };
68
-
69
- blockNode = React.createRef();
70
-
71
- render() {
72
- const { intl, blockProps, draginfo, extraControls, disabled, children } =
73
- this.props;
74
-
75
- const {
76
- allowedBlocks,
77
- block,
78
- data,
79
- onSelectBlock,
80
- onDeleteBlock,
81
- onMutateBlock,
82
- onInsertBlock,
83
- selected,
84
- } = blockProps;
85
- const type = data['@type'];
86
- const { disableNewBlocks } = data;
87
- const dragVisible = !data.fixed;
88
- const visible = selected;
89
-
90
- const required = isBoolean(data.required)
91
- ? data.required
92
- : includes(config.blocks.requiredBlocks, type);
93
-
94
- const styles = buildStyleClassNamesFromData(data.styles);
95
-
96
- // Get editing instructions from block settings or props
97
- let instructions = data?.instructions?.data || data?.instructions;
98
- if (!instructions || instructions === '<p><br/></p>') {
99
- instructions = '';
100
- }
101
-
102
- return (
103
- <div ref={this.blockNode}>
104
- <div
105
- ref={draginfo?.innerRef}
106
- {...(selected ? draginfo?.draggableProps : null)}
107
- className={cx(`block-editor-${data['@type']}`, styles, {
108
- [data.align]: data.align,
109
- })}
110
- >
111
- {(!selected || !visible || disabled) && (
112
- <div
113
- style={{
114
- display: 'none',
115
- // keep react-beautiful-dnd happy
116
- }}
117
- {...draginfo.dragHandleProps}
118
- ></div>
119
- )}
120
- {visible && (
121
- <div className="block-toolbar">
122
- {instructions ? extraControls : ''}
123
-
124
- {!disabled && (
125
- <>
126
- <div
127
- style={{
128
- display: dragVisible ? 'inline-block' : 'none',
129
- }}
130
- {...draginfo.dragHandleProps}
131
- className="drag handle wrapper-group-block"
132
- >
133
- <Button icon basic title="Drag and drop">
134
- <Icon name={dragSVG} size="19px" />
135
- </Button>
136
- </div>
137
-
138
- {!disableNewBlocks && !blockHasValue(data) && (
139
- <Button
140
- icon
141
- basic
142
- title={intl.formatMessage(messages.addBlock)}
143
- onClick={() => {
144
- this.setState((prevState) => ({
145
- addNewBlockOpened: !prevState.addNewBlockOpened,
146
- }));
147
- }}
148
- className="group-block-add-button"
149
- >
150
- <Icon name={addSVG} className="" size="19px" />
151
- </Button>
152
- )}
153
- {!required && (
154
- <Button
155
- icon
156
- basic
157
- title={intl.formatMessage(messages.removeBlock)}
158
- onClick={() => onDeleteBlock(block)}
159
- className="delete-button-group-block"
160
- aria-label={intl.formatMessage(messages.delete)}
161
- >
162
- <Icon name={trashSVG} size="19px" />
163
- </Button>
164
- )}
165
- {this.state.addNewBlockOpened && (
166
- <BlockChooser
167
- onMutateBlock={(id, value) => {
168
- onMutateBlock(id, value);
169
- this.setState({ addNewBlockOpened: false });
170
- }}
171
- onInsertBlock={(id, value) => {
172
- onSelectBlock(onInsertBlock(id, value));
173
- this.setState({ addNewBlockOpened: false });
174
- }}
175
- currentBlock={block}
176
- allowedBlocks={allowedBlocks}
177
- />
178
- )}
179
- </>
180
- )}
181
- </div>
182
- )}
183
-
184
- <div
185
- className={cx('ui drag block wrapper inner', type, {
186
- multiSelected: this.props.multiSelected,
187
- })}
188
- >
189
- {children}
190
- </div>
191
- </div>
192
- </div>
193
- );
194
- }
195
- }
196
-
197
- export default injectIntl(EditBlockWrapper);
@@ -1,58 +0,0 @@
1
- import React from 'react';
2
- import { fireEvent, render } from '@testing-library/react';
3
- import { Provider } from 'react-intl-redux';
4
- import EditBlockWrapper from './EditBlockWrapper';
5
- import configureStore from 'redux-mock-store';
6
- import '@testing-library/jest-dom/extend-expect';
7
-
8
- const mockDragInfo = {
9
- innerRef: {
10
- current: {
11
- childMethod: jest.fn(),
12
- },
13
- },
14
- draggableProps: {},
15
- dragHandleProps: {},
16
- };
17
-
18
- const mockStore = configureStore();
19
- const store = mockStore({
20
- intl: {
21
- locale: 'en',
22
- messages: {},
23
- },
24
- });
25
-
26
- describe('EditBlockWrapper', () => {
27
- const mockBlockProps = {
28
- allowedBlocks: [],
29
- block: 'mockBlock',
30
- data: {},
31
- onSelectBlock: jest.fn(),
32
- onDeleteBlock: jest.fn(),
33
- onMutateBlock: jest.fn(),
34
- onInsertBlock: jest.fn(),
35
- selected: true,
36
- };
37
-
38
- it('renders children', () => {
39
- const { getByText } = render(
40
- <Provider store={store}>
41
- <EditBlockWrapper blockProps={mockBlockProps} draginfo={mockDragInfo}>
42
- <div>Test child</div>
43
- </EditBlockWrapper>
44
- </Provider>,
45
- );
46
- expect(getByText('Test child')).toBeInTheDocument();
47
- });
48
-
49
- it('calls onDeleteBlock when delete button is clicked', () => {
50
- const { getByTitle } = render(
51
- <Provider store={store}>
52
- <EditBlockWrapper blockProps={mockBlockProps} draginfo={mockDragInfo} />
53
- </Provider>,
54
- );
55
- fireEvent.click(getByTitle('Remove block'));
56
- expect(mockBlockProps.onDeleteBlock).toHaveBeenCalledWith('mockBlock');
57
- });
58
- });