@eeacms/volto-eea-website-theme 1.27.1 → 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('./../../../');
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'],
@@ -54,3 +57,9 @@ module.exports = {
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,6 +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.28.0](https://github.com/eea/volto-eea-website-theme/compare/1.27.2...1.28.0) - 16 February 2024
8
+
9
+ #### :bug: Bug Fixes
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)]
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)]
14
+
15
+ #### :house: Internal changes
16
+
17
+ - chore: package.json [Alin Voinea - [`08beb70`](https://github.com/eea/volto-eea-website-theme/commit/08beb706d9021a89c80acc5aa7c94350195f7de7)]
18
+
19
+ #### :hammer_and_wrench: Others
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)]
24
+ - fix tests [Razvan - [`042330b`](https://github.com/eea/volto-eea-website-theme/commit/042330bc97d32ffe7ba769b4f2453f71cffed706)]
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
+
7
28
  ### [1.27.1](https://github.com/eea/volto-eea-website-theme/compare/1.27.0...1.27.1) - 18 January 2024
8
29
 
9
30
  #### :bug: Bug Fixes
@@ -93,11 +114,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
93
114
  - test: Add real image to cypress test [Alin Voinea - [`4ff591a`](https://github.com/eea/volto-eea-website-theme/commit/4ff591ae3318c9588b4e2114582c0fa6cfdf31ae)]
94
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)]
95
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)]
96
- - test: [JENKINS] Use java17 for sonarqube scanner [valentinab25 - [`6a3be30`](https://github.com/eea/volto-eea-website-theme/commit/6a3be3092589411af7808a235f76de5222fd3868)]
97
- - test: [JENKINS] Run cypress in started frontend container [valentinab25 - [`c3978f2`](https://github.com/eea/volto-eea-website-theme/commit/c3978f23375ef066e9fd6f6c2e34ba6c1c058f69)]
98
- - test: [JENKINS] Add cpu limit on cypress docker [valentinab25 - [`f672779`](https://github.com/eea/volto-eea-website-theme/commit/f672779e845bec9240ccc901e9f53ec80c5a1819)]
99
- - test: [JENKINS] Increase shm-size to cypress docker [valentinab25 - [`ae5d8e3`](https://github.com/eea/volto-eea-website-theme/commit/ae5d8e3f4e04dc2808d47ce2ee886e1b23b528da)]
100
- - test: [JENKINS] Improve cypress time [valentinab25 - [`170ff0c`](https://github.com/eea/volto-eea-website-theme/commit/170ff0c8e3b30e69479bdf1117e811fea94f1027)]
101
117
  ### [1.23.0](https://github.com/eea/volto-eea-website-theme/compare/1.22.1...1.23.0) - 2 November 2023
102
118
 
103
119
  #### :rocket: New Features
@@ -110,7 +126,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
110
126
 
111
127
  #### :house: Internal changes
112
128
 
113
- - chore: [JENKINS] Refactor automated testing [valentinab25 - [`f28fce3`](https://github.com/eea/volto-eea-website-theme/commit/f28fce3d1eb815f95fb9aa40de42b10b7e8e30c5)]
114
129
  - chore: husky, lint-staged use fixed versions [valentinab25 - [`6d15088`](https://github.com/eea/volto-eea-website-theme/commit/6d150886c5aeb2ca0b569270486e60f7cc274e2c)]
115
130
  - chore:volto 16 in tests, update docs, fix stylelint overrides [valentinab25 - [`20c0323`](https://github.com/eea/volto-eea-website-theme/commit/20c032380b33c0077c869a05136f93e2fb68e5d4)]
116
131
 
@@ -296,7 +311,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
296
311
 
297
312
  #### :house: Internal changes
298
313
 
299
- - chore: [JENKINS] Deprecate circularity website [valentinab25 - [`370dcbf`](https://github.com/eea/volto-eea-website-theme/commit/370dcbfbf1a8135ce7b1b3b271b004552a631837)]
300
314
 
301
315
  #### :hammer_and_wrench: Others
302
316
 
@@ -452,7 +466,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
452
466
 
453
467
  #### :hammer_and_wrench: Others
454
468
 
455
- - Add Sonarqube tag using eea-website-frontend addons list [EEA Jenkins - [`6c5e2f8`](https://github.com/eea/volto-eea-website-theme/commit/6c5e2f80456e2061d9e9c15fd0a0b91b9ac70568)]
456
469
  ### [1.9.1](https://github.com/eea/volto-eea-website-theme/compare/1.9.0...1.9.1) - 28 February 2023
457
470
 
458
471
  #### :bug: Bug Fixes
@@ -599,7 +612,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
599
612
 
600
613
  - For some reasons types is a string [Alin Voinea - [`3769a09`](https://github.com/eea/volto-eea-website-theme/commit/3769a0981181d5b633f3498daebbe96be8b4b833)]
601
614
  - Fix(redirect): o.filter - refs #157627 [Alin Voinea - [`deb23da`](https://github.com/eea/volto-eea-website-theme/commit/deb23da846444cc96539697fd798429ae0abe89e)]
602
- - Add Sonarqube tag using advisory-board-frontend addons list [EEA Jenkins - [`f1fffc5`](https://github.com/eea/volto-eea-website-theme/commit/f1fffc5db96725440863d545580b4e76cce4b796)]
603
615
  ### [1.5.0](https://github.com/eea/volto-eea-website-theme/compare/1.4.2...1.5.0) - 9 January 2023
604
616
 
605
617
  #### :hammer_and_wrench: Others
@@ -633,7 +645,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
633
645
 
634
646
  - Release 1.4.0 [Alin Voinea - [`bd42a0d`](https://github.com/eea/volto-eea-website-theme/commit/bd42a0d26e928cac5d99933194755da3db06b341)]
635
647
  - bump version to use as volto-eea-design-system [David Ichim - [`f4be047`](https://github.com/eea/volto-eea-website-theme/commit/f4be047328b46399b03b612d378b18aaf82e7dc1)]
636
- - Add Sonarqube tag using advisory-board-frontend addons list [EEA Jenkins - [`9b7cfef`](https://github.com/eea/volto-eea-website-theme/commit/9b7cfefb4d34fc1c948015e491feb370f9795bd8)]
637
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)]
638
649
  ### [1.3.2](https://github.com/eea/volto-eea-website-theme/compare/1.3.1...1.3.2) - 16 December 2022
639
650
 
@@ -643,7 +654,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
643
654
 
644
655
  #### :hammer_and_wrench: Others
645
656
 
646
- - Add Sonarqube tag using cca-frontend addons list [EEA Jenkins - [`a43c658`](https://github.com/eea/volto-eea-website-theme/commit/a43c658a7920c8df95e763b9a637f38ce77eba2c)]
647
657
  - Better razzle.config [Tiberiu Ichim - [`81dbf48`](https://github.com/eea/volto-eea-website-theme/commit/81dbf48815fb27facb4f82c9b764540fdf188b2e)]
648
658
  - Better razzle.config [Tiberiu Ichim - [`7bc9da2`](https://github.com/eea/volto-eea-website-theme/commit/7bc9da2cd837ab62a95cd29979cdd9b0055b7d67)]
649
659
  ### [1.3.1](https://github.com/eea/volto-eea-website-theme/compare/1.3.0...1.3.1) - 28 November 2022
@@ -654,7 +664,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
654
664
 
655
665
  #### :hammer_and_wrench: Others
656
666
 
657
- - yarn 3 [Alin Voinea - [`ea7a709`](https://github.com/eea/volto-eea-website-theme/commit/ea7a7094945312776e9b6f44e371178603e92139)]
658
667
  ### [1.3.0](https://github.com/eea/volto-eea-website-theme/compare/1.2.0...1.3.0) - 22 November 2022
659
668
 
660
669
  #### :rocket: New Features
@@ -695,7 +704,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
695
704
  - Add subsite class to body [Tiberiu Ichim - [`74d700f`](https://github.com/eea/volto-eea-website-theme/commit/74d700fbfd6249a8604762a7e4e49cce857db0f3)]
696
705
  - Add subsite info to header [Tiberiu Ichim - [`47daf8b`](https://github.com/eea/volto-eea-website-theme/commit/47daf8bb6374a1222040626b19d4154df7ba1b83)]
697
706
  - fix eslint [Miu Razvan - [`eb8d0a7`](https://github.com/eea/volto-eea-website-theme/commit/eb8d0a790bc70c0aae256c6ff35f63c4885f338e)]
698
- - Add Sonarqube tag using circularity-frontend addons list [EEA Jenkins - [`cc578a4`](https://github.com/eea/volto-eea-website-theme/commit/cc578a413b205a8e61e091fab3a88f94cedefc89)]
699
707
  ### [1.1.0](https://github.com/eea/volto-eea-website-theme/compare/1.0.0...1.1.0) - 28 October 2022
700
708
 
701
709
  #### :nail_care: Enhancements
@@ -743,7 +751,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
743
751
 
744
752
  #### :hammer_and_wrench: Others
745
753
 
746
- - Add Sonarqube tag using eea-website-frontend addons list [EEA Jenkins - [`33b56ac`](https://github.com/eea/volto-eea-website-theme/commit/33b56acb13fbaf0c5b79e8fc6e13c4b699c79c90)]
747
754
  ### [0.7.3](https://github.com/eea/volto-eea-website-theme/compare/0.7.2...0.7.3) - 22 September 2022
748
755
 
749
756
  #### :hammer_and_wrench: Others
@@ -1011,7 +1018,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1011
1018
  - Header refactor, add custom logo #5 [ichim-david - [`4950235`](https://github.com/eea/volto-eea-website-theme/commit/49502358105437cfeac3b144e6d301cb59aa2346)]
1012
1019
  - Update footer.config with new publication card component [ichim-david - [`2e38e9a`](https://github.com/eea/volto-eea-website-theme/commit/2e38e9a417f835009d60c80d4eb4b30229f55e45)]
1013
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)]
1014
- - Add Sonarqube tag using eea-website-frontend addons list [EEA Jenkins - [`da8ceb6`](https://github.com/eea/volto-eea-website-theme/commit/da8ceb68ea68bfbc9504e48ccd4d68277f11ab9a)]
1015
1021
  - use breadcrumbs from eea-design-system [nileshgulia1 - [`db2f9e9`](https://github.com/eea/volto-eea-website-theme/commit/db2f9e9a4327420a3cce9a9903cd88549b129eab)]
1016
1022
  - Update theme.config [ichim-david - [`8eca4f4`](https://github.com/eea/volto-eea-website-theme/commit/8eca4f40397a4aeca6d39029c92db78968d37064)]
1017
1023
  - Added keyContent component to theme.config [ichim-david - [`d86f202`](https://github.com/eea/volto-eea-website-theme/commit/d86f202d0274d839487a88b51cae9a0e899beb23)]
@@ -1053,5 +1059,4 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
1053
1059
 
1054
1060
  #### :hammer_and_wrench: Others
1055
1061
 
1056
- - yarn bootstrap [Alin Voinea - [`6995e9e`](https://github.com/eea/volto-eea-website-theme/commit/6995e9e091f21fdbbdffa8a44fc0e2c626f6d46a)]
1057
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.1",
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 {
@@ -26,8 +27,6 @@ import contentBoxSVG from './icons/content-box.svg';
26
27
  import okMiddleware from './middleware/ok';
27
28
  import voltoCustomMiddleware from './middleware/voltoCustom';
28
29
  import installSlate from './slate';
29
- import installReducers from './reducers';
30
- import installMiddlewares from './middleware';
31
30
 
32
31
  import { nanoid } from '@plone/volto-slate/utils';
33
32
  import { v4 as uuid } from 'uuid';
@@ -226,6 +225,20 @@ const applyConfig = (config) => {
226
225
  ...config.views.errorViews,
227
226
  '404': NotFound,
228
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
+ }
229
242
  // Apply accordion block customization
230
243
  if (config.blocks.blocksConfig.accordion) {
231
244
  config.blocks.blocksConfig.accordion.titleIcons = {
@@ -528,10 +541,7 @@ const applyConfig = (config) => {
528
541
  });
529
542
 
530
543
  // Custom blocks: Title
531
- return [installCustomTitle, installMiddlewares, installReducers].reduce(
532
- (acc, apply) => apply(acc),
533
- config,
534
- );
544
+ return [installCustomTitle].reduce((acc, apply) => apply(acc), config);
535
545
  };
536
546
 
537
547
  export default applyConfig;
package/src/index.test.js CHANGED
@@ -45,10 +45,6 @@ jest.mock(
45
45
  jest.mock('@plone/volto/components', () => ({
46
46
  Icon: 'MockedIcon',
47
47
  }));
48
- jest.mock(
49
- '@eeacms/volto-eea-website-theme/components/theme/AppExtras/RemoveSchema',
50
- () => 'MockedRemoveSchema',
51
- );
52
48
 
53
49
  global.__SERVER__ = true;
54
50
 
@@ -144,7 +140,6 @@ describe('applyConfig', () => {
144
140
  { match: '', component: 'MockedDraftBackground' },
145
141
  { match: '', component: 'MockedSubsiteClass' },
146
142
  { match: '', component: BaseTag },
147
- { match: '*', exclude: '/**/diff', component: 'MockedRemoveSchema' },
148
143
  ]);
149
144
  expect(config.settings.available_colors).toEqual(eea.colors);
150
145
  expect(config.settings.hasLanguageDropdown).toBe(false);
@@ -303,7 +298,6 @@ describe('applyConfig', () => {
303
298
  { match: '', component: 'MockedDraftBackground' },
304
299
  { match: '', component: 'MockedSubsiteClass' },
305
300
  { match: '', component: BaseTag },
306
- { match: '*', exclude: '/**/diff', component: 'MockedRemoveSchema' },
307
301
  ]);
308
302
  expect(config.settings.available_colors).toEqual(eea.colors);
309
303
  expect(config.settings.hasLanguageDropdown).toBe(false);
@@ -1,29 +0,0 @@
1
- import { useEffect } from 'react';
2
- import { connect } from 'react-redux';
3
- import { removeSchema } from '@eeacms/volto-eea-website-theme/actions';
4
- import { flattenToAppURL } from '@plone/volto/helpers';
5
-
6
- function RemoveSchema({ removeSchema, contentLoading, content, schema }) {
7
- useEffect(() => {
8
- if (
9
- !schema.loading &&
10
- !contentLoading &&
11
- schema.schema &&
12
- (!content ||
13
- (content && schema.contentUrl !== flattenToAppURL(content['@id'])))
14
- ) {
15
- removeSchema();
16
- }
17
- }, [removeSchema, content, contentLoading, schema]);
18
-
19
- return null;
20
- }
21
-
22
- export default connect(
23
- (state) => ({
24
- content: state.content.data,
25
- contentLoading: state.content.get.loading,
26
- schema: state.schema,
27
- }),
28
- { removeSchema },
29
- )(RemoveSchema);
@@ -1,11 +0,0 @@
1
- export default function apiEnhancer(middlewares) {
2
- return [
3
- () => (next) => (action) => {
4
- if (action.request) {
5
- return next({ ...action, _request: action.request });
6
- }
7
- return next(action);
8
- },
9
- ...middlewares,
10
- ];
11
- }
@@ -1,10 +0,0 @@
1
- import apiEnhancer from './apiEnhancer';
2
-
3
- export default function applyConfig(config) {
4
- config.settings.storeExtenders = [
5
- ...(config.settings.storeExtenders || []),
6
- apiEnhancer,
7
- ];
8
-
9
- return config;
10
- }
@@ -1,18 +0,0 @@
1
- import RemoveSchema from '@eeacms/volto-eea-website-theme/components/theme/AppExtras/RemoveSchema';
2
-
3
- import schema from './schema';
4
-
5
- export default function applyConfig(config) {
6
- config.addonReducers.schema = schema;
7
-
8
- config.settings.appExtras = [
9
- ...(config.settings.appExtras || []),
10
- {
11
- match: '*',
12
- exclude: '/**/diff',
13
- component: RemoveSchema,
14
- },
15
- ];
16
-
17
- return config;
18
- }
@@ -1,48 +0,0 @@
1
- import voltoSchema from '@plone/volto/reducers/schema/schema';
2
-
3
- const initialState = {
4
- contentUrl: null,
5
- contentType: null,
6
- error: null,
7
- loaded: false,
8
- loading: false,
9
- schema: null,
10
- post: {
11
- loaded: false,
12
- loading: false,
13
- error: null,
14
- },
15
- update: {
16
- loaded: false,
17
- loading: false,
18
- error: null,
19
- },
20
- put: {
21
- loaded: false,
22
- loading: false,
23
- error: null,
24
- },
25
- };
26
-
27
- export default function schema(state = initialState, action = {}) {
28
- if (action.type === 'REMOVE_SCHEMA') {
29
- return {
30
- ...state,
31
- error: null,
32
- loading: false,
33
- loaded: false,
34
- schema: null,
35
- };
36
- }
37
-
38
- if (action.type === 'GET_SCHEMA_SUCCESS') {
39
- const [contentUrl, contentType] = action._request.path.split('/@types/');
40
- return {
41
- ...voltoSchema(state, action),
42
- contentUrl,
43
- contentType,
44
- };
45
- }
46
-
47
- return voltoSchema(state, action);
48
- }