@eeacms/volto-eea-website-theme 1.27.2 → 1.28.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
@@ -1,40 +1,43 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
-
4
- const projectRootPath = fs.existsSync('./project')
5
- ? fs.realpathSync('./project')
6
- : fs.realpathSync(__dirname + '/../../../');
7
- const packageJson = require(path.join(projectRootPath, 'package.json'));
8
- const jsConfig = require(path.join(projectRootPath, 'jsconfig.json')).compilerOptions;
9
-
10
- const pathsConfig = jsConfig.paths;
3
+ const projectRootPath = fs.realpathSync(__dirname + '/../../../');
11
4
 
12
5
  let voltoPath = path.join(projectRootPath, 'node_modules/@plone/volto');
6
+ let configFile;
7
+ if (fs.existsSync(`${projectRootPath}/tsconfig.json`))
8
+ configFile = `${projectRootPath}/tsconfig.json`;
9
+ else if (fs.existsSync(`${projectRootPath}/jsconfig.json`))
10
+ configFile = `${projectRootPath}/jsconfig.json`;
11
+
12
+ if (configFile) {
13
+ const jsConfig = require(configFile).compilerOptions;
14
+ const pathsConfig = jsConfig.paths;
15
+ if (pathsConfig['@plone/volto'])
16
+ voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`;
17
+ }
13
18
 
14
- Object.keys(pathsConfig).forEach(pkg => {
15
- if (pkg === '@plone/volto') {
16
- voltoPath = `./${jsConfig.baseUrl}/${pathsConfig[pkg][0]}`;
17
- }
18
- });
19
19
  const AddonConfigurationRegistry = require(`${voltoPath}/addon-registry.js`);
20
20
  const reg = new AddonConfigurationRegistry(projectRootPath);
21
21
 
22
22
  // Extends ESlint configuration for adding the aliases to `src` directories in Volto addons
23
- const addonAliases = Object.keys(reg.packages).map(o => [
23
+ const addonAliases = Object.keys(reg.packages).map((o) => [
24
24
  o,
25
25
  reg.packages[o].modulePath,
26
26
  ]);
27
27
 
28
+ const addonExtenders = reg.getEslintExtenders().map((m) => require(m));
28
29
 
29
- module.exports = {
30
- extends: `${projectRootPath}/node_modules/@plone/volto/.eslintrc`,
30
+ const defaultConfig = {
31
+ extends: `${voltoPath}/.eslintrc`,
31
32
  settings: {
32
33
  'import/resolver': {
33
34
  alias: {
34
35
  map: [
35
36
  ['@plone/volto', '@plone/volto/src'],
37
+ ['@plone/volto-slate', '@plone/volto/packages/volto-slate/src'],
36
38
  ...addonAliases,
37
39
  ['@package', `${__dirname}/src`],
40
+ ['@root', `${__dirname}/src`],
38
41
  ['~', `${__dirname}/src`],
39
42
  ],
40
43
  extensions: ['.js', '.jsx', '.json'],
@@ -51,6 +54,12 @@ module.exports = {
51
54
  allowReferrer: true,
52
55
  },
53
56
  ],
54
- }
57
+ },
55
58
  };
56
59
 
60
+ const config = addonExtenders.reduce(
61
+ (acc, extender) => extender.modify(acc),
62
+ defaultConfig,
63
+ );
64
+
65
+ module.exports = config;
package/CHANGELOG.md CHANGED
@@ -4,16 +4,27 @@ 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
- ### [1.27.2](https://github.com/eea/volto-eea-website-theme/compare/1.27.1...1.27.2) - 23 January 2024
7
+ ### [1.28.0](https://github.com/eea/volto-eea-website-theme/compare/1.27.2...1.28.0) - 16 February 2024
8
8
 
9
9
  #### :bug: Bug Fixes
10
10
 
11
+ - fix(toc): make toc work, refs #265201 [Razvan - [`507adc2`](https://github.com/eea/volto-eea-website-theme/commit/507adc29f0a2e144933b06dfcf0856f1ac7efc98)]
12
+ - fix: volto slate when used in metadata block with SlateJSONField - refs #264239 [Miu Razvan - [`51682c4`](https://github.com/eea/volto-eea-website-theme/commit/51682c42001f6aa3433feff62c5f8536283de990)]
11
13
  - fix(lint): service so that it work with editor and command line tool [David Ichim - [`ad43bc2`](https://github.com/eea/volto-eea-website-theme/commit/ad43bc2f9bfc3e272d30b35db9d4b160a8edcbec)]
12
14
 
15
+ #### :house: Internal changes
16
+
17
+ - chore: package.json [Alin Voinea - [`08beb70`](https://github.com/eea/volto-eea-website-theme/commit/08beb706d9021a89c80acc5aa7c94350195f7de7)]
18
+
13
19
  #### :hammer_and_wrench: Others
14
20
 
21
+ - bump version [Razvan - [`721e939`](https://github.com/eea/volto-eea-website-theme/commit/721e939d12e324b459ebfa78a2e656ee7142a3d6)]
22
+ - merge master into this branch [Razvan - [`586c8f9`](https://github.com/eea/volto-eea-website-theme/commit/586c8f910bac55a043bd8dda60e9444bd2ae1663)]
23
+ - test: Update jest,Jenkinsfile,lint to volto-addons-template PR30 [valentinab25 - [`c4dbd28`](https://github.com/eea/volto-eea-website-theme/commit/c4dbd289358205bc2d849aab7edb11ccf3b89cee)]
15
24
  - fix tests [Razvan - [`042330b`](https://github.com/eea/volto-eea-website-theme/commit/042330bc97d32ffe7ba769b4f2453f71cffed706)]
16
25
  - remove RemoveSchema logic [Razvan - [`08d10f8`](https://github.com/eea/volto-eea-website-theme/commit/08d10f8bf6f75478260e4e4c66d7316ba87b907a)]
26
+ ### [1.27.2](https://github.com/eea/volto-eea-website-theme/compare/1.27.1...1.27.2) - 24 January 2024
27
+
17
28
  ### [1.27.1](https://github.com/eea/volto-eea-website-theme/compare/1.27.0...1.27.1) - 18 January 2024
18
29
 
19
30
  #### :bug: Bug Fixes
@@ -103,11 +114,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
103
114
  - test: Add real image to cypress test [Alin Voinea - [`4ff591a`](https://github.com/eea/volto-eea-website-theme/commit/4ff591ae3318c9588b4e2114582c0fa6cfdf31ae)]
104
115
  - test: Add cypress tests for Image block styling position and align [Alin Voinea - [`7341ef7`](https://github.com/eea/volto-eea-website-theme/commit/7341ef7b92714fc0cc3ab0c31c39033e7b3e19e7)]
105
116
  - Revert "change(tests): commented out rss test since title block config is missing" [Alin Voinea - [`fb61191`](https://github.com/eea/volto-eea-website-theme/commit/fb611918d6ca380b89b594f283dcf9f685a4b294)]
106
- - test: [JENKINS] Use java17 for sonarqube scanner [valentinab25 - [`6a3be30`](https://github.com/eea/volto-eea-website-theme/commit/6a3be3092589411af7808a235f76de5222fd3868)]
107
- - test: [JENKINS] Run cypress in started frontend container [valentinab25 - [`c3978f2`](https://github.com/eea/volto-eea-website-theme/commit/c3978f23375ef066e9fd6f6c2e34ba6c1c058f69)]
108
- - test: [JENKINS] Add cpu limit on cypress docker [valentinab25 - [`f672779`](https://github.com/eea/volto-eea-website-theme/commit/f672779e845bec9240ccc901e9f53ec80c5a1819)]
109
- - test: [JENKINS] Increase shm-size to cypress docker [valentinab25 - [`ae5d8e3`](https://github.com/eea/volto-eea-website-theme/commit/ae5d8e3f4e04dc2808d47ce2ee886e1b23b528da)]
110
- - test: [JENKINS] Improve cypress time [valentinab25 - [`170ff0c`](https://github.com/eea/volto-eea-website-theme/commit/170ff0c8e3b30e69479bdf1117e811fea94f1027)]
111
117
  ### [1.23.0](https://github.com/eea/volto-eea-website-theme/compare/1.22.1...1.23.0) - 2 November 2023
112
118
 
113
119
  #### :rocket: New Features
@@ -120,7 +126,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
120
126
 
121
127
  #### :house: Internal changes
122
128
 
123
- - chore: [JENKINS] Refactor automated testing [valentinab25 - [`f28fce3`](https://github.com/eea/volto-eea-website-theme/commit/f28fce3d1eb815f95fb9aa40de42b10b7e8e30c5)]
124
129
  - chore: husky, lint-staged use fixed versions [valentinab25 - [`6d15088`](https://github.com/eea/volto-eea-website-theme/commit/6d150886c5aeb2ca0b569270486e60f7cc274e2c)]
125
130
  - chore:volto 16 in tests, update docs, fix stylelint overrides [valentinab25 - [`20c0323`](https://github.com/eea/volto-eea-website-theme/commit/20c032380b33c0077c869a05136f93e2fb68e5d4)]
126
131
 
@@ -306,7 +311,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
306
311
 
307
312
  #### :house: Internal changes
308
313
 
309
- - chore: [JENKINS] Deprecate circularity website [valentinab25 - [`370dcbf`](https://github.com/eea/volto-eea-website-theme/commit/370dcbfbf1a8135ce7b1b3b271b004552a631837)]
310
314
 
311
315
  #### :hammer_and_wrench: Others
312
316
 
@@ -462,7 +466,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
462
466
 
463
467
  #### :hammer_and_wrench: Others
464
468
 
465
- - Add Sonarqube tag using eea-website-frontend addons list [EEA Jenkins - [`6c5e2f8`](https://github.com/eea/volto-eea-website-theme/commit/6c5e2f80456e2061d9e9c15fd0a0b91b9ac70568)]
466
469
  ### [1.9.1](https://github.com/eea/volto-eea-website-theme/compare/1.9.0...1.9.1) - 28 February 2023
467
470
 
468
471
  #### :bug: Bug Fixes
@@ -609,7 +612,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
609
612
 
610
613
  - For some reasons types is a string [Alin Voinea - [`3769a09`](https://github.com/eea/volto-eea-website-theme/commit/3769a0981181d5b633f3498daebbe96be8b4b833)]
611
614
  - Fix(redirect): o.filter - refs #157627 [Alin Voinea - [`deb23da`](https://github.com/eea/volto-eea-website-theme/commit/deb23da846444cc96539697fd798429ae0abe89e)]
612
- - Add Sonarqube tag using advisory-board-frontend addons list [EEA Jenkins - [`f1fffc5`](https://github.com/eea/volto-eea-website-theme/commit/f1fffc5db96725440863d545580b4e76cce4b796)]
613
615
  ### [1.5.0](https://github.com/eea/volto-eea-website-theme/compare/1.4.2...1.5.0) - 9 January 2023
614
616
 
615
617
  #### :hammer_and_wrench: Others
@@ -643,7 +645,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
643
645
 
644
646
  - Release 1.4.0 [Alin Voinea - [`bd42a0d`](https://github.com/eea/volto-eea-website-theme/commit/bd42a0d26e928cac5d99933194755da3db06b341)]
645
647
  - bump version to use as volto-eea-design-system [David Ichim - [`f4be047`](https://github.com/eea/volto-eea-website-theme/commit/f4be047328b46399b03b612d378b18aaf82e7dc1)]
646
- - Add Sonarqube tag using advisory-board-frontend addons list [EEA Jenkins - [`9b7cfef`](https://github.com/eea/volto-eea-website-theme/commit/9b7cfefb4d34fc1c948015e491feb370f9795bd8)]
647
648
  - test(Jenkins): Run tests and cypress with latest canary @plone/volto [Alin Voinea - [`df252a9`](https://github.com/eea/volto-eea-website-theme/commit/df252a9bfed0bb86cadf53c59dd1603b1e2cd822)]
648
649
  ### [1.3.2](https://github.com/eea/volto-eea-website-theme/compare/1.3.1...1.3.2) - 16 December 2022
649
650
 
@@ -653,7 +654,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
653
654
 
654
655
  #### :hammer_and_wrench: Others
655
656
 
656
- - Add Sonarqube tag using cca-frontend addons list [EEA Jenkins - [`a43c658`](https://github.com/eea/volto-eea-website-theme/commit/a43c658a7920c8df95e763b9a637f38ce77eba2c)]
657
657
  - Better razzle.config [Tiberiu Ichim - [`81dbf48`](https://github.com/eea/volto-eea-website-theme/commit/81dbf48815fb27facb4f82c9b764540fdf188b2e)]
658
658
  - Better razzle.config [Tiberiu Ichim - [`7bc9da2`](https://github.com/eea/volto-eea-website-theme/commit/7bc9da2cd837ab62a95cd29979cdd9b0055b7d67)]
659
659
  ### [1.3.1](https://github.com/eea/volto-eea-website-theme/compare/1.3.0...1.3.1) - 28 November 2022
@@ -664,7 +664,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
664
664
 
665
665
  #### :hammer_and_wrench: Others
666
666
 
667
- - yarn 3 [Alin Voinea - [`ea7a709`](https://github.com/eea/volto-eea-website-theme/commit/ea7a7094945312776e9b6f44e371178603e92139)]
668
667
  ### [1.3.0](https://github.com/eea/volto-eea-website-theme/compare/1.2.0...1.3.0) - 22 November 2022
669
668
 
670
669
  #### :rocket: New Features
@@ -705,7 +704,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
705
704
  - Add subsite class to body [Tiberiu Ichim - [`74d700f`](https://github.com/eea/volto-eea-website-theme/commit/74d700fbfd6249a8604762a7e4e49cce857db0f3)]
706
705
  - Add subsite info to header [Tiberiu Ichim - [`47daf8b`](https://github.com/eea/volto-eea-website-theme/commit/47daf8bb6374a1222040626b19d4154df7ba1b83)]
707
706
  - fix eslint [Miu Razvan - [`eb8d0a7`](https://github.com/eea/volto-eea-website-theme/commit/eb8d0a790bc70c0aae256c6ff35f63c4885f338e)]
708
- - Add Sonarqube tag using circularity-frontend addons list [EEA Jenkins - [`cc578a4`](https://github.com/eea/volto-eea-website-theme/commit/cc578a413b205a8e61e091fab3a88f94cedefc89)]
709
707
  ### [1.1.0](https://github.com/eea/volto-eea-website-theme/compare/1.0.0...1.1.0) - 28 October 2022
710
708
 
711
709
  #### :nail_care: Enhancements
@@ -753,7 +751,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
753
751
 
754
752
  #### :hammer_and_wrench: Others
755
753
 
756
- - Add Sonarqube tag using eea-website-frontend addons list [EEA Jenkins - [`33b56ac`](https://github.com/eea/volto-eea-website-theme/commit/33b56acb13fbaf0c5b79e8fc6e13c4b699c79c90)]
757
754
  ### [0.7.3](https://github.com/eea/volto-eea-website-theme/compare/0.7.2...0.7.3) - 22 September 2022
758
755
 
759
756
  #### :hammer_and_wrench: Others
@@ -1021,7 +1018,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1021
1018
  - Header refactor, add custom logo #5 [ichim-david - [`4950235`](https://github.com/eea/volto-eea-website-theme/commit/49502358105437cfeac3b144e6d301cb59aa2346)]
1022
1019
  - Update footer.config with new publication card component [ichim-david - [`2e38e9a`](https://github.com/eea/volto-eea-website-theme/commit/2e38e9a417f835009d60c80d4eb4b30229f55e45)]
1023
1020
  - feature(breadcrumbs): implement eea-design-system breadcrumb as Volto component #32 #7 [ichim-david - [`181af41`](https://github.com/eea/volto-eea-website-theme/commit/181af4125ce2b9ddac56dab4723cb11c26633221)]
1024
- - Add Sonarqube tag using eea-website-frontend addons list [EEA Jenkins - [`da8ceb6`](https://github.com/eea/volto-eea-website-theme/commit/da8ceb68ea68bfbc9504e48ccd4d68277f11ab9a)]
1025
1021
  - use breadcrumbs from eea-design-system [nileshgulia1 - [`db2f9e9`](https://github.com/eea/volto-eea-website-theme/commit/db2f9e9a4327420a3cce9a9903cd88549b129eab)]
1026
1022
  - Update theme.config [ichim-david - [`8eca4f4`](https://github.com/eea/volto-eea-website-theme/commit/8eca4f40397a4aeca6d39029c92db78968d37064)]
1027
1023
  - Added keyContent component to theme.config [ichim-david - [`d86f202`](https://github.com/eea/volto-eea-website-theme/commit/d86f202d0274d839487a88b51cae9a0e899beb23)]
@@ -1063,5 +1059,4 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1063
1059
 
1064
1060
  #### :hammer_and_wrench: Others
1065
1061
 
1066
- - yarn bootstrap [Alin Voinea - [`6995e9e`](https://github.com/eea/volto-eea-website-theme/commit/6995e9e091f21fdbbdffa8a44fc0e2c626f6d46a)]
1067
1062
  - Initial commit [Alin Voinea - [`6a9c03a`](https://github.com/eea/volto-eea-website-theme/commit/6a9c03a7cebe71ca87e82cf58c42904063e9d8d3)]
package/README.md CHANGED
@@ -27,6 +27,8 @@ See [Storybook](https://eea.github.io/eea-storybook/).
27
27
 
28
28
  ## Volto customizations
29
29
 
30
+ - `volto-slate/editor/SlateEditor` -> [ref](https://taskman.eionet.europa.eu/issues/264239#note-11) When two slates looks at the same prop changing one slate and updating the other should be handled properly. This change makes replacing the old value of slate work in sync with the other slates that watches the same prop.
31
+
30
32
  - `volto/components/manage/Sidebar/SidebarPopup` -> https://github.com/plone/volto/pull/5520
31
33
 
32
34
  ## Getting started
@@ -1,3 +1,5 @@
1
+ require('dotenv').config({ path: __dirname + '/.env' })
2
+
1
3
  module.exports = {
2
4
  testMatch: ['**/src/addons/**/?(*.)+(spec|test).[jt]s?(x)'],
3
5
  collectCoverageFrom: [
@@ -9,31 +11,38 @@ module.exports = {
9
11
  '@plone/volto/cypress': '<rootDir>/node_modules/@plone/volto/cypress',
10
12
  '@plone/volto/babel': '<rootDir>/node_modules/@plone/volto/babel',
11
13
  '@plone/volto/(.*)$': '<rootDir>/node_modules/@plone/volto/src/$1',
12
- '@package/(.*)$': '<rootDir>/src/$1',
13
- '@root/(.*)$': '<rootDir>/src/$1',
14
+ '@package/(.*)$': '<rootDir>/node_modules/@plone/volto/src/$1',
15
+ '@root/(.*)$': '<rootDir>/node_modules/@plone/volto/src/$1',
14
16
  '@plone/volto-quanta/(.*)$': '<rootDir>/src/addons/volto-quanta/src/$1',
15
17
  '@eeacms/(.*?)/(.*)$': '<rootDir>/node_modules/@eeacms/$1/src/$2',
16
- '@plone/volto-slate':
18
+ '@plone/volto-slate$':
17
19
  '<rootDir>/node_modules/@plone/volto/packages/volto-slate/src',
20
+ '@plone/volto-slate/(.*)$':
21
+ '<rootDir>/node_modules/@plone/volto/packages/volto-slate/src/$1',
18
22
  '~/(.*)$': '<rootDir>/src/$1',
19
23
  'load-volto-addons':
20
24
  '<rootDir>/node_modules/@plone/volto/jest-addons-loader.js',
21
25
  },
26
+ transformIgnorePatterns: [
27
+ '/node_modules/(?!(@plone|@root|@package|@eeacms)/).*/',
28
+ ],
22
29
  transform: {
23
30
  '^.+\\.js(x)?$': 'babel-jest',
24
31
  '^.+\\.(png)$': 'jest-file',
25
32
  '^.+\\.(jpg)$': 'jest-file',
26
33
  '^.+\\.(svg)$': './node_modules/@plone/volto/jest-svgsystem-transform.js',
27
34
  },
28
- transformIgnorePatterns: [
29
- 'node_modules/(?!@eeacms)/volto-eea-design-system/ui',
30
- ],
31
35
  coverageThreshold: {
32
36
  global: {
33
- branches: 0,
34
- functions: 0,
35
- lines: 0,
36
- statements: 0,
37
+ branches: 5,
38
+ functions: 5,
39
+ lines: 5,
40
+ statements: 5,
37
41
  },
38
42
  },
39
- };
43
+ ...(process.env.JEST_USE_SETUP === 'ON' && {
44
+ setupFilesAfterEnv: [
45
+ '<rootDir>/node_modules/@eeacms/volto-eea-website-theme/jest.setup.js',
46
+ ],
47
+ }),
48
+ }
package/jest.setup.js ADDED
@@ -0,0 +1,65 @@
1
+ import { jest } from '@jest/globals';
2
+ import configureStore from 'redux-mock-store';
3
+ import thunk from 'redux-thunk';
4
+ import { blocksConfig } from '@plone/volto/config/Blocks';
5
+ import installSlate from '@plone/volto-slate/index';
6
+
7
+ var mockSemanticComponents = jest.requireActual('semantic-ui-react');
8
+ var mockComponents = jest.requireActual('@plone/volto/components');
9
+ var config = jest.requireActual('@plone/volto/registry').default;
10
+
11
+ config.blocks.blocksConfig = {
12
+ ...blocksConfig,
13
+ ...config.blocks.blocksConfig,
14
+ };
15
+
16
+ jest.doMock('semantic-ui-react', () => ({
17
+ __esModule: true,
18
+ ...mockSemanticComponents,
19
+ Popup: ({ content, trigger }) => {
20
+ return (
21
+ <div className="popup">
22
+ <div className="trigger">{trigger}</div>
23
+ <div className="content">{content}</div>
24
+ </div>
25
+ );
26
+ },
27
+ }));
28
+
29
+ jest.doMock('@plone/volto/components', () => {
30
+ return {
31
+ __esModule: true,
32
+ ...mockComponents,
33
+ SidebarPortal: ({ children }) => <div id="sidebar">{children}</div>,
34
+ };
35
+ });
36
+
37
+ jest.doMock('@plone/volto/registry', () =>
38
+ [installSlate].reduce((acc, apply) => apply(acc), config),
39
+ );
40
+
41
+ const mockStore = configureStore([thunk]);
42
+
43
+ global.fetch = jest.fn(() =>
44
+ Promise.resolve({
45
+ json: () => Promise.resolve({}),
46
+ }),
47
+ );
48
+
49
+ global.store = mockStore({
50
+ intl: {
51
+ locale: 'en',
52
+ messages: {},
53
+ formatMessage: jest.fn(),
54
+ },
55
+ content: {
56
+ create: {},
57
+ subrequests: [],
58
+ },
59
+ connected_data_parameters: {},
60
+ screen: {
61
+ page: {
62
+ width: 768,
63
+ },
64
+ },
65
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-eea-website-theme",
3
- "version": "1.27.2",
3
+ "version": "1.28.0",
4
4
  "description": "@eeacms/volto-eea-website-theme: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -32,6 +32,7 @@
32
32
  "@cypress/code-coverage": "^3.10.0",
33
33
  "@plone/scripts": "*",
34
34
  "babel-plugin-transform-class-properties": "^6.24.1",
35
+ "dotenv": "^16.3.2",
35
36
  "husky": "^8.0.3",
36
37
  "lint-staged": "^14.0.1",
37
38
  "md5": "^2.3.0",
@@ -78,4 +79,4 @@
78
79
  "cypress:open": "make cypress-open",
79
80
  "prepare": "husky install"
80
81
  }
81
- }
82
+ }
@@ -0,0 +1,404 @@
1
+ import ReactDOM from 'react-dom';
2
+ import cx from 'classnames';
3
+ import { isEqual, cloneDeep } from 'lodash';
4
+ import { Transforms, Editor, Point } from 'slate'; // , Transforms
5
+ import { Slate, Editable, ReactEditor } from 'slate-react';
6
+ import React, { Component } from 'react'; // , useState
7
+ import { v4 as uuid } from 'uuid';
8
+
9
+ import config from '@plone/volto/registry';
10
+
11
+ import { Element, Leaf } from './render';
12
+
13
+ import withTestingFeatures from '@plone/volto-slate/editor/extensions/withTestingFeatures';
14
+ import {
15
+ makeEditor,
16
+ toggleInlineFormat,
17
+ toggleMark,
18
+ parseDefaultSelection,
19
+ } from '@plone/volto-slate/utils';
20
+ import { InlineToolbar } from '@plone/volto-slate/editor/ui';
21
+ import EditorContext from '@plone/volto-slate/editor/EditorContext';
22
+
23
+ import isHotkey from 'is-hotkey';
24
+
25
+ import '@plone/volto-slate/editor/less/editor.less';
26
+
27
+ import Toolbar from '@plone/volto-slate/editor/ui/Toolbar';
28
+
29
+ const handleHotKeys = (editor, event, config) => {
30
+ let wasHotkey = false;
31
+
32
+ for (const hk of Object.entries(config.hotkeys)) {
33
+ const [shortcut, { format, type }] = hk;
34
+ if (isHotkey(shortcut, event)) {
35
+ event.preventDefault();
36
+
37
+ if (type === 'inline') {
38
+ toggleInlineFormat(editor, format);
39
+ } else {
40
+ // type === 'mark'
41
+ toggleMark(editor, format);
42
+ }
43
+
44
+ wasHotkey = true;
45
+ }
46
+ }
47
+
48
+ return wasHotkey;
49
+ };
50
+
51
+ function resetNodes(editor, options = {}) {
52
+ const children = [...editor.children];
53
+
54
+ children.forEach((node) =>
55
+ editor.apply({ type: 'remove_node', path: [0], node }),
56
+ );
57
+
58
+ if (options.nodes) {
59
+ options.nodes.forEach((node, i) =>
60
+ editor.apply({ type: 'insert_node', path: [i], node: node }),
61
+ );
62
+ }
63
+
64
+ const point =
65
+ options.at && Point.isPoint(options.at)
66
+ ? options.at
67
+ : Editor.end(editor, []);
68
+
69
+ if (point) {
70
+ Transforms.select(editor, point);
71
+ }
72
+ }
73
+
74
+ // TODO: implement onFocus
75
+ class SlateEditor extends Component {
76
+ constructor(props) {
77
+ super(props);
78
+
79
+ this.createEditor = this.createEditor.bind(this);
80
+ this.multiDecorator = this.multiDecorator.bind(this);
81
+ this.handleChange = this.handleChange.bind(this);
82
+ this.getSavedSelection = this.getSavedSelection.bind(this);
83
+ this.setSavedSelection = this.setSavedSelection.bind(this);
84
+
85
+ this.savedSelection = null;
86
+
87
+ const uid = uuid(); // used to namespace the editor's plugins
88
+
89
+ this.slateSettings = props.slateSettings || config.settings.slate;
90
+
91
+ this.initialValue = cloneDeep(
92
+ this.props.value || this.slateSettings.defaultValue(),
93
+ );
94
+
95
+ this.state = {
96
+ editor: this.createEditor(uid),
97
+ showExpandedToolbar: config.settings.slate.showExpandedToolbar,
98
+ internalValue: this.initialValue,
99
+ uid,
100
+ };
101
+
102
+ this.editor = null;
103
+ this.selectionTimeout = null;
104
+ }
105
+
106
+ getSavedSelection() {
107
+ return this.savedSelection;
108
+ }
109
+ setSavedSelection(selection) {
110
+ this.savedSelection = selection;
111
+ }
112
+
113
+ createEditor(uid) {
114
+ // extensions are "editor plugins" or "editor wrappers". It's a similar
115
+ // similar to OOP inheritance, where a callable creates a new copy of the
116
+ // editor, while replacing or adding new capabilities to that editor.
117
+ // Extensions are purely JS, no React components.
118
+ const editor = makeEditor({ extensions: this.props.extensions });
119
+
120
+ // When the editor loses focus it no longer has a valid selections. This
121
+ // makes it impossible to have complex types of interactions (like filling
122
+ // in another text box, operating a select menu, etc). For this reason we
123
+ // save the active selection
124
+
125
+ editor.getSavedSelection = this.getSavedSelection;
126
+ editor.setSavedSelection = this.setSavedSelection;
127
+ editor.uid = uid || this.state.uid;
128
+
129
+ return editor;
130
+ }
131
+
132
+ handleChange(value) {
133
+ ReactDOM.unstable_batchedUpdates(() => {
134
+ const newValue = cloneDeep(value);
135
+ this.setState({ internalValue: newValue });
136
+ if (this.props.onChange && !isEqual(newValue, this.props.value)) {
137
+ this.props.onChange(newValue, this.editor);
138
+ }
139
+ });
140
+ }
141
+
142
+ multiDecorator([node, path]) {
143
+ // Decorations (such as higlighting node types, selection, etc).
144
+ const { runtimeDecorators = [] } = this.slateSettings;
145
+ return runtimeDecorators.reduce(
146
+ (acc, deco) => deco(this.state.editor, [node, path], acc),
147
+ [],
148
+ );
149
+ }
150
+
151
+ componentDidMount() {
152
+ // watch the dom change
153
+
154
+ if (this.props.selected) {
155
+ let focused = true;
156
+ try {
157
+ focused = ReactEditor.isFocused(this.state.editor);
158
+ } catch {}
159
+ if (!focused) {
160
+ setTimeout(() => {
161
+ try {
162
+ ReactEditor.focus(this.state.editor);
163
+ } catch {}
164
+ }, 100); // flush
165
+ }
166
+
167
+ this.state.editor.normalize({ force: true });
168
+ }
169
+ }
170
+
171
+ componentWillUnmount() {
172
+ this.isUnmounted = true;
173
+ }
174
+
175
+ componentDidUpdate(prevProps) {
176
+ if (!isEqual(prevProps.extensions, this.props.extensions)) {
177
+ this.setState({ editor: this.createEditor() });
178
+ return;
179
+ }
180
+
181
+ if (
182
+ this.props.value &&
183
+ !isEqual(this.props.value, this.state.internalValue)
184
+ ) {
185
+ const newValue = cloneDeep(this.props.value);
186
+ const { editor } = this.state;
187
+
188
+ resetNodes(editor, { nodes: newValue });
189
+
190
+ this.setState({
191
+ internalValue: newValue,
192
+ });
193
+
194
+ if (this.props.defaultSelection) {
195
+ const selection = parseDefaultSelection(
196
+ editor,
197
+ this.props.defaultSelection,
198
+ );
199
+
200
+ ReactEditor.focus(editor);
201
+ Transforms.select(editor, selection);
202
+ } else {
203
+ Transforms.select(editor, Editor.end(editor, []));
204
+ }
205
+ return;
206
+ }
207
+
208
+ const { editor } = this.state;
209
+
210
+ if (!prevProps.selected && this.props.selected) {
211
+ // if the SlateEditor becomes selected from unselected
212
+
213
+ if (window.getSelection().type === 'None') {
214
+ // TODO: why is this condition checked?
215
+ Transforms.select(
216
+ this.state.editor,
217
+ Editor.range(this.state.editor, Editor.start(this.state.editor, [])),
218
+ );
219
+ }
220
+
221
+ ReactEditor.focus(this.state.editor);
222
+ }
223
+
224
+ if (this.props.selected && this.props.onUpdate) {
225
+ this.props.onUpdate(editor);
226
+ }
227
+ }
228
+
229
+ shouldComponentUpdate(nextProps, nextState) {
230
+ const { selected = true, value, readOnly } = nextProps;
231
+ const res =
232
+ selected ||
233
+ this.props.selected !== selected ||
234
+ this.props.readOnly !== readOnly ||
235
+ !isEqual(value, this.props.value);
236
+ return res;
237
+ }
238
+
239
+ render() {
240
+ const {
241
+ selected,
242
+ placeholder,
243
+ onKeyDown,
244
+ testingEditorRef,
245
+ readOnly,
246
+ className,
247
+ renderExtensions = [],
248
+ editableProps = {},
249
+ } = this.props;
250
+ const slateSettings = this.slateSettings;
251
+
252
+ // renderExtensions is needed because the editor is memoized, so if these
253
+ // extensions need an updated state (for example to insert updated
254
+ // blockProps) then we need to always wrap the editor with them
255
+ const editor = renderExtensions.reduce(
256
+ (acc, apply) => apply(acc),
257
+ this.state.editor,
258
+ );
259
+
260
+ // Reset selection if field is reset
261
+ if (
262
+ editor.selection &&
263
+ this.props.value?.length === 1 &&
264
+ this.props.value[0].children.length === 1 &&
265
+ this.props.value[0].children[0].text === ''
266
+ ) {
267
+ Transforms.select(editor, {
268
+ anchor: { path: [0, 0], offset: 0 },
269
+ focus: { path: [0, 0], offset: 0 },
270
+ });
271
+ }
272
+ this.editor = editor;
273
+
274
+ if (testingEditorRef) {
275
+ testingEditorRef.current = editor;
276
+ }
277
+
278
+ // debug-values are `data-` HTML attributes in withTestingFeatures HOC
279
+
280
+ return (
281
+ <div
282
+ {...this.props['debug-values']}
283
+ className={cx('slate-editor', {
284
+ 'show-toolbar': this.state.showExpandedToolbar,
285
+ selected,
286
+ })}
287
+ tabIndex={-1}
288
+ >
289
+ <EditorContext.Provider value={editor}>
290
+ <Slate
291
+ editor={editor}
292
+ initialValue={this.initialValue}
293
+ onChange={this.handleChange}
294
+ >
295
+ {selected ? (
296
+ <>
297
+ <InlineToolbar
298
+ editor={editor}
299
+ className={className}
300
+ slateSettings={this.props.slateSettings}
301
+ />
302
+ {Object.keys(slateSettings.elementToolbarButtons).map(
303
+ (t, i) => {
304
+ return (
305
+ <Toolbar elementType={t} key={i}>
306
+ {slateSettings.elementToolbarButtons[t].map(
307
+ (Btn, b) => {
308
+ return <Btn editor={editor} key={b} />;
309
+ },
310
+ )}
311
+ </Toolbar>
312
+ );
313
+ },
314
+ )}
315
+ </>
316
+ ) : (
317
+ ''
318
+ )}
319
+ <Editable
320
+ tabIndex={this.props.tabIndex || 0}
321
+ readOnly={readOnly}
322
+ placeholder={placeholder}
323
+ renderElement={(props) => <Element {...props} />}
324
+ renderLeaf={(props) => <Leaf {...props} />}
325
+ decorate={this.multiDecorator}
326
+ spellCheck={false}
327
+ scrollSelectionIntoView={
328
+ slateSettings.scrollIntoView ? undefined : () => null
329
+ }
330
+ onBlur={() => {
331
+ this.props.onBlur && this.props.onBlur();
332
+ return null;
333
+ }}
334
+ onClick={this.props.onClick}
335
+ onSelect={(e) => {
336
+ if (!selected && this.props.onFocus) {
337
+ // we can't overwrite the onFocus of Editable, as the onFocus
338
+ // in Slate has too much builtin behaviour that's not
339
+ // accessible otherwise. Instead we try to detect such an
340
+ // event based on observing selected state
341
+ if (!editor.selection) {
342
+ setTimeout(() => {
343
+ this.props.onFocus();
344
+ }, 100); // TODO: why 100 is chosen here?
345
+ }
346
+ }
347
+
348
+ if (this.selectionTimeout) clearTimeout(this.selectionTimeout);
349
+ this.selectionTimeout = setTimeout(() => {
350
+ if (
351
+ editor.selection &&
352
+ !isEqual(editor.selection, this.savedSelection) &&
353
+ !this.isUnmounted
354
+ ) {
355
+ this.setState((state) => ({ update: !this.state.update }));
356
+ this.setSavedSelection(
357
+ JSON.parse(JSON.stringify(editor.selection)),
358
+ );
359
+ }
360
+ }, 200);
361
+ }}
362
+ onKeyDown={(event) => {
363
+ const handled = handleHotKeys(editor, event, slateSettings);
364
+ if (handled) return;
365
+ onKeyDown && onKeyDown({ editor, event });
366
+ }}
367
+ {...editableProps}
368
+ />
369
+ {selected &&
370
+ slateSettings.persistentHelpers.map((Helper, i) => {
371
+ return <Helper key={i} editor={editor} />;
372
+ })}
373
+ {this.props.debug ? (
374
+ <ul>
375
+ <li>{selected ? 'selected' : 'no-selected'}</li>
376
+ <li>
377
+ {ReactEditor.isFocused(editor) ? 'focused' : 'unfocused'}
378
+ </li>
379
+ <li>
380
+ savedSelection: {JSON.stringify(editor.getSavedSelection())}
381
+ </li>
382
+ <li>live selection: {JSON.stringify(editor.selection)}</li>
383
+ <li>children: {JSON.stringify(editor.children)}</li>
384
+ </ul>
385
+ ) : (
386
+ ''
387
+ )}
388
+ {this.props.children}
389
+ </Slate>
390
+ </EditorContext.Provider>
391
+ </div>
392
+ );
393
+ }
394
+ }
395
+
396
+ SlateEditor.defaultProps = {
397
+ extensions: [],
398
+ className: '',
399
+ };
400
+
401
+ // May be needed to wrap in React.memo(), it used to be wrapped in connect()
402
+ export default __CLIENT__ && window?.Cypress
403
+ ? withTestingFeatures(SlateEditor)
404
+ : SlateEditor;
package/src/index.js CHANGED
@@ -8,6 +8,7 @@ import { TokenWidget } from '@eeacms/volto-eea-website-theme/components/theme/Wi
8
8
  import { TopicsWidget } from '@eeacms/volto-eea-website-theme/components/theme/Widgets/TopicsWidget';
9
9
  import { Icon } from '@plone/volto/components';
10
10
  import { getBlocks } from '@plone/volto/helpers';
11
+ import { serializeNodesToText } from '@plone/volto-slate/editor/render';
11
12
  import Tag from '@eeacms/volto-eea-design-system/ui/Tag/Tag';
12
13
 
13
14
  import {
@@ -224,6 +225,20 @@ const applyConfig = (config) => {
224
225
  ...config.views.errorViews,
225
226
  '404': NotFound,
226
227
  };
228
+ // Apply slate text block customization
229
+ if (config.blocks.blocksConfig.slate) {
230
+ config.blocks.blocksConfig.slate.tocEntry = (block = {}) => {
231
+ const { value, override_toc, entry_text, level } = block;
232
+ const plaintext =
233
+ serializeNodesToText(block.value || []) || block.plaintext;
234
+ const type = value?.[0]?.type;
235
+ return override_toc && level
236
+ ? [parseInt(level.slice(1)), entry_text]
237
+ : config.settings.slate.topLevelTargetElements.includes(type)
238
+ ? [parseInt(type.slice(1)), plaintext]
239
+ : null;
240
+ };
241
+ }
227
242
  // Apply accordion block customization
228
243
  if (config.blocks.blocksConfig.accordion) {
229
244
  config.blocks.blocksConfig.accordion.titleIcons = {