@eeacms/volto-eea-website-theme 1.32.1 → 1.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/package.json +2 -2
- package/src/components/manage/Blocks/LayoutSettings/LayoutSettingsView.test.jsx +68 -0
- package/src/components/manage/Blocks/LayoutSettings/schema.js +1 -0
- package/src/components/theme/Widgets/CreatableSelectWidget.jsx +304 -0
- package/src/components/theme/Widgets/CreatableSelectWidget.test.jsx +89 -0
- package/src/customizations/volto/components/manage/Add/Add.jsx +498 -0
- package/src/customizations/volto/components/manage/Add/readme.md +1 -0
- package/src/customizations/volto/components/manage/Blocks/Image/View.jsx +1 -1
- package/src/helpers/schema-utils.js +7 -1
- package/src/index.js +3 -21
- package/src/index.test.js +2 -0
package/CHANGELOG.md
CHANGED
@@ -4,6 +4,31 @@ 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.33.1](https://github.com/eea/volto-eea-website-theme/compare/1.33.0...1.33.1) - 4 April 2024
|
8
|
+
|
9
|
+
#### :bug: Bug Fixes
|
10
|
+
|
11
|
+
- fix: adding page with an empty title - refs #268074 [dobri1408 - [`6443924`](https://github.com/eea/volto-eea-website-theme/commit/64439247ab34eef693d4192c778e330267b25fd0)]
|
12
|
+
|
13
|
+
### [1.33.0](https://github.com/eea/volto-eea-website-theme/compare/1.32.1...1.33.0) - 3 April 2024
|
14
|
+
|
15
|
+
#### :rocket: New Features
|
16
|
+
|
17
|
+
- feat(columnsBlock): move tocEntries definition to volto-columns-block [Miu Razvan - [`eb55ff8`](https://github.com/eea/volto-eea-website-theme/commit/eb55ff8753e443c83fd4a8bb3dca8c2b2a78a782)]
|
18
|
+
|
19
|
+
#### :bug: Bug Fixes
|
20
|
+
|
21
|
+
- fix(toc): use the correct function to render entries in toc [Miu Razvan - [`869ae7c`](https://github.com/eea/volto-eea-website-theme/commit/869ae7cbe2ec555ebfe563d66bb00e5bc80651c2)]
|
22
|
+
|
23
|
+
#### :house: Internal changes
|
24
|
+
|
25
|
+
- chore: Cleanup package.json [alin - [`c5a1c37`](https://github.com/eea/volto-eea-website-theme/commit/c5a1c370eec27a0ebea9df51dde12040580def2f)]
|
26
|
+
|
27
|
+
#### :hammer_and_wrench: Others
|
28
|
+
|
29
|
+
- Update index.js to fix jslint issue [ichim-david - [`89b9cef`](https://github.com/eea/volto-eea-website-theme/commit/89b9cefbe35f780bdd07fa6f44234fbfc2fe7bb0)]
|
30
|
+
- Update package.json [ichim-david - [`a0ecadf`](https://github.com/eea/volto-eea-website-theme/commit/a0ecadf6efcdeb0fec4ee533d27a6c6787029373)]
|
31
|
+
- Revert "fix(blocks): Allow image block urls to be external (#214)" [David Ichim - [`2bbc620`](https://github.com/eea/volto-eea-website-theme/commit/2bbc620d0a375d69ba7982c8e3113628365bb8da)]
|
7
32
|
### [1.32.1](https://github.com/eea/volto-eea-website-theme/compare/1.32.0...1.32.1) - 26 March 2024
|
8
33
|
|
9
34
|
#### :rocket: New Features
|
@@ -112,6 +137,8 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
112
137
|
|
113
138
|
- bump version [Razvan - [`721e939`](https://github.com/eea/volto-eea-website-theme/commit/721e939d12e324b459ebfa78a2e656ee7142a3d6)]
|
114
139
|
- merge master into this branch [Razvan - [`586c8f9`](https://github.com/eea/volto-eea-website-theme/commit/586c8f910bac55a043bd8dda60e9444bd2ae1663)]
|
140
|
+
- Add Sonarqube tag using freshwater-frontend addons list [EEA Jenkins - [`fd90044`](https://github.com/eea/volto-eea-website-theme/commit/fd9004442a9d1d465f7601ecdefe3e23c61e6a9c)]
|
141
|
+
- Add Sonarqube tag using insitu-frontend addons list [EEA Jenkins - [`4bc3dd3`](https://github.com/eea/volto-eea-website-theme/commit/4bc3dd3ae412a66befd04b5b80fab3716c929240)]
|
115
142
|
- test: Update jest,Jenkinsfile,lint to volto-addons-template PR30 [valentinab25 - [`c4dbd28`](https://github.com/eea/volto-eea-website-theme/commit/c4dbd289358205bc2d849aab7edb11ccf3b89cee)]
|
116
143
|
- fix tests [Razvan - [`042330b`](https://github.com/eea/volto-eea-website-theme/commit/042330bc97d32ffe7ba769b4f2453f71cffed706)]
|
117
144
|
- remove RemoveSchema logic [Razvan - [`08d10f8`](https://github.com/eea/volto-eea-website-theme/commit/08d10f8bf6f75478260e4e4c66d7316ba87b907a)]
|
@@ -206,6 +233,11 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
206
233
|
- test: Add real image to cypress test [Alin Voinea - [`4ff591a`](https://github.com/eea/volto-eea-website-theme/commit/4ff591ae3318c9588b4e2114582c0fa6cfdf31ae)]
|
207
234
|
- test: Add cypress tests for Image block styling position and align [Alin Voinea - [`7341ef7`](https://github.com/eea/volto-eea-website-theme/commit/7341ef7b92714fc0cc3ab0c31c39033e7b3e19e7)]
|
208
235
|
- 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)]
|
236
|
+
- test: [JENKINS] Use java17 for sonarqube scanner [valentinab25 - [`6a3be30`](https://github.com/eea/volto-eea-website-theme/commit/6a3be3092589411af7808a235f76de5222fd3868)]
|
237
|
+
- test: [JENKINS] Run cypress in started frontend container [valentinab25 - [`c3978f2`](https://github.com/eea/volto-eea-website-theme/commit/c3978f23375ef066e9fd6f6c2e34ba6c1c058f69)]
|
238
|
+
- test: [JENKINS] Add cpu limit on cypress docker [valentinab25 - [`f672779`](https://github.com/eea/volto-eea-website-theme/commit/f672779e845bec9240ccc901e9f53ec80c5a1819)]
|
239
|
+
- test: [JENKINS] Increase shm-size to cypress docker [valentinab25 - [`ae5d8e3`](https://github.com/eea/volto-eea-website-theme/commit/ae5d8e3f4e04dc2808d47ce2ee886e1b23b528da)]
|
240
|
+
- test: [JENKINS] Improve cypress time [valentinab25 - [`170ff0c`](https://github.com/eea/volto-eea-website-theme/commit/170ff0c8e3b30e69479bdf1117e811fea94f1027)]
|
209
241
|
### [1.23.0](https://github.com/eea/volto-eea-website-theme/compare/1.22.1...1.23.0) - 2 November 2023
|
210
242
|
|
211
243
|
#### :rocket: New Features
|
@@ -218,6 +250,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
218
250
|
|
219
251
|
#### :house: Internal changes
|
220
252
|
|
253
|
+
- chore: [JENKINS] Refactor automated testing [valentinab25 - [`f28fce3`](https://github.com/eea/volto-eea-website-theme/commit/f28fce3d1eb815f95fb9aa40de42b10b7e8e30c5)]
|
221
254
|
- chore: husky, lint-staged use fixed versions [valentinab25 - [`6d15088`](https://github.com/eea/volto-eea-website-theme/commit/6d150886c5aeb2ca0b569270486e60f7cc274e2c)]
|
222
255
|
- chore:volto 16 in tests, update docs, fix stylelint overrides [valentinab25 - [`20c0323`](https://github.com/eea/volto-eea-website-theme/commit/20c032380b33c0077c869a05136f93e2fb68e5d4)]
|
223
256
|
|
@@ -403,6 +436,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
403
436
|
|
404
437
|
#### :house: Internal changes
|
405
438
|
|
439
|
+
- chore: [JENKINS] Deprecate circularity website [valentinab25 - [`370dcbf`](https://github.com/eea/volto-eea-website-theme/commit/370dcbfbf1a8135ce7b1b3b271b004552a631837)]
|
406
440
|
|
407
441
|
#### :hammer_and_wrench: Others
|
408
442
|
|
@@ -558,6 +592,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
558
592
|
|
559
593
|
#### :hammer_and_wrench: Others
|
560
594
|
|
595
|
+
- Add Sonarqube tag using eea-website-frontend addons list [EEA Jenkins - [`6c5e2f8`](https://github.com/eea/volto-eea-website-theme/commit/6c5e2f80456e2061d9e9c15fd0a0b91b9ac70568)]
|
561
596
|
### [1.9.1](https://github.com/eea/volto-eea-website-theme/compare/1.9.0...1.9.1) - 28 February 2023
|
562
597
|
|
563
598
|
#### :bug: Bug Fixes
|
@@ -704,6 +739,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
704
739
|
|
705
740
|
- For some reasons types is a string [Alin Voinea - [`3769a09`](https://github.com/eea/volto-eea-website-theme/commit/3769a0981181d5b633f3498daebbe96be8b4b833)]
|
706
741
|
- Fix(redirect): o.filter - refs #157627 [Alin Voinea - [`deb23da`](https://github.com/eea/volto-eea-website-theme/commit/deb23da846444cc96539697fd798429ae0abe89e)]
|
742
|
+
- Add Sonarqube tag using advisory-board-frontend addons list [EEA Jenkins - [`f1fffc5`](https://github.com/eea/volto-eea-website-theme/commit/f1fffc5db96725440863d545580b4e76cce4b796)]
|
707
743
|
### [1.5.0](https://github.com/eea/volto-eea-website-theme/compare/1.4.2...1.5.0) - 9 January 2023
|
708
744
|
|
709
745
|
#### :hammer_and_wrench: Others
|
@@ -737,6 +773,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
737
773
|
|
738
774
|
- Release 1.4.0 [Alin Voinea - [`bd42a0d`](https://github.com/eea/volto-eea-website-theme/commit/bd42a0d26e928cac5d99933194755da3db06b341)]
|
739
775
|
- bump version to use as volto-eea-design-system [David Ichim - [`f4be047`](https://github.com/eea/volto-eea-website-theme/commit/f4be047328b46399b03b612d378b18aaf82e7dc1)]
|
776
|
+
- Add Sonarqube tag using advisory-board-frontend addons list [EEA Jenkins - [`9b7cfef`](https://github.com/eea/volto-eea-website-theme/commit/9b7cfefb4d34fc1c948015e491feb370f9795bd8)]
|
740
777
|
- test(Jenkins): Run tests and cypress with latest canary @plone/volto [Alin Voinea - [`df252a9`](https://github.com/eea/volto-eea-website-theme/commit/df252a9bfed0bb86cadf53c59dd1603b1e2cd822)]
|
741
778
|
### [1.3.2](https://github.com/eea/volto-eea-website-theme/compare/1.3.1...1.3.2) - 16 December 2022
|
742
779
|
|
@@ -746,6 +783,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
746
783
|
|
747
784
|
#### :hammer_and_wrench: Others
|
748
785
|
|
786
|
+
- Add Sonarqube tag using cca-frontend addons list [EEA Jenkins - [`a43c658`](https://github.com/eea/volto-eea-website-theme/commit/a43c658a7920c8df95e763b9a637f38ce77eba2c)]
|
749
787
|
- Better razzle.config [Tiberiu Ichim - [`81dbf48`](https://github.com/eea/volto-eea-website-theme/commit/81dbf48815fb27facb4f82c9b764540fdf188b2e)]
|
750
788
|
- Better razzle.config [Tiberiu Ichim - [`7bc9da2`](https://github.com/eea/volto-eea-website-theme/commit/7bc9da2cd837ab62a95cd29979cdd9b0055b7d67)]
|
751
789
|
### [1.3.1](https://github.com/eea/volto-eea-website-theme/compare/1.3.0...1.3.1) - 28 November 2022
|
@@ -756,6 +794,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
756
794
|
|
757
795
|
#### :hammer_and_wrench: Others
|
758
796
|
|
797
|
+
- yarn 3 [Alin Voinea - [`ea7a709`](https://github.com/eea/volto-eea-website-theme/commit/ea7a7094945312776e9b6f44e371178603e92139)]
|
759
798
|
### [1.3.0](https://github.com/eea/volto-eea-website-theme/compare/1.2.0...1.3.0) - 22 November 2022
|
760
799
|
|
761
800
|
#### :rocket: New Features
|
@@ -796,6 +835,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
796
835
|
- Add subsite class to body [Tiberiu Ichim - [`74d700f`](https://github.com/eea/volto-eea-website-theme/commit/74d700fbfd6249a8604762a7e4e49cce857db0f3)]
|
797
836
|
- Add subsite info to header [Tiberiu Ichim - [`47daf8b`](https://github.com/eea/volto-eea-website-theme/commit/47daf8bb6374a1222040626b19d4154df7ba1b83)]
|
798
837
|
- fix eslint [Miu Razvan - [`eb8d0a7`](https://github.com/eea/volto-eea-website-theme/commit/eb8d0a790bc70c0aae256c6ff35f63c4885f338e)]
|
838
|
+
- Add Sonarqube tag using circularity-frontend addons list [EEA Jenkins - [`cc578a4`](https://github.com/eea/volto-eea-website-theme/commit/cc578a413b205a8e61e091fab3a88f94cedefc89)]
|
799
839
|
### [1.1.0](https://github.com/eea/volto-eea-website-theme/compare/1.0.0...1.1.0) - 28 October 2022
|
800
840
|
|
801
841
|
#### :nail_care: Enhancements
|
@@ -843,6 +883,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
843
883
|
|
844
884
|
#### :hammer_and_wrench: Others
|
845
885
|
|
886
|
+
- Add Sonarqube tag using eea-website-frontend addons list [EEA Jenkins - [`33b56ac`](https://github.com/eea/volto-eea-website-theme/commit/33b56acb13fbaf0c5b79e8fc6e13c4b699c79c90)]
|
846
887
|
### [0.7.3](https://github.com/eea/volto-eea-website-theme/compare/0.7.2...0.7.3) - 22 September 2022
|
847
888
|
|
848
889
|
#### :hammer_and_wrench: Others
|
@@ -1110,6 +1151,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
1110
1151
|
- Header refactor, add custom logo #5 [ichim-david - [`4950235`](https://github.com/eea/volto-eea-website-theme/commit/49502358105437cfeac3b144e6d301cb59aa2346)]
|
1111
1152
|
- Update footer.config with new publication card component [ichim-david - [`2e38e9a`](https://github.com/eea/volto-eea-website-theme/commit/2e38e9a417f835009d60c80d4eb4b30229f55e45)]
|
1112
1153
|
- 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)]
|
1154
|
+
- Add Sonarqube tag using eea-website-frontend addons list [EEA Jenkins - [`da8ceb6`](https://github.com/eea/volto-eea-website-theme/commit/da8ceb68ea68bfbc9504e48ccd4d68277f11ab9a)]
|
1113
1155
|
- use breadcrumbs from eea-design-system [nileshgulia1 - [`db2f9e9`](https://github.com/eea/volto-eea-website-theme/commit/db2f9e9a4327420a3cce9a9903cd88549b129eab)]
|
1114
1156
|
- Update theme.config [ichim-david - [`8eca4f4`](https://github.com/eea/volto-eea-website-theme/commit/8eca4f40397a4aeca6d39029c92db78968d37064)]
|
1115
1157
|
- Added keyContent component to theme.config [ichim-david - [`d86f202`](https://github.com/eea/volto-eea-website-theme/commit/d86f202d0274d839487a88b51cae9a0e899beb23)]
|
@@ -1151,4 +1193,5 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
1151
1193
|
|
1152
1194
|
#### :hammer_and_wrench: Others
|
1153
1195
|
|
1196
|
+
- yarn bootstrap [Alin Voinea - [`6995e9e`](https://github.com/eea/volto-eea-website-theme/commit/6995e9e091f21fdbbdffa8a44fc0e2c626f6d46a)]
|
1154
1197
|
- Initial commit [Alin Voinea - [`6a9c03a`](https://github.com/eea/volto-eea-website-theme/commit/6a9c03a7cebe71ca87e82cf58c42904063e9d8d3)]
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@eeacms/volto-eea-website-theme",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.33.1",
|
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",
|
@@ -79,4 +79,4 @@
|
|
79
79
|
"cypress:open": "make cypress-open",
|
80
80
|
"prepare": "husky install"
|
81
81
|
}
|
82
|
-
}
|
82
|
+
}
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { render } from '@testing-library/react';
|
3
|
+
import { Provider } from 'react-intl-redux';
|
4
|
+
import configureStore from 'redux-mock-store';
|
5
|
+
import { Router } from 'react-router-dom';
|
6
|
+
import { createMemoryHistory } from 'history';
|
7
|
+
import LayoutSettingsView from './LayoutSettingsView';
|
8
|
+
|
9
|
+
const mockStore = configureStore();
|
10
|
+
let history = createMemoryHistory();
|
11
|
+
|
12
|
+
describe('LayoutSettingsView Component', () => {
|
13
|
+
it('renders without crashing', () => {
|
14
|
+
const store = mockStore({
|
15
|
+
intl: {
|
16
|
+
locale: 'en',
|
17
|
+
messages: {},
|
18
|
+
},
|
19
|
+
});
|
20
|
+
|
21
|
+
const data = {
|
22
|
+
'@layout': 'e28ec238-4cd7-4b72-8025-66da44a6062f',
|
23
|
+
'@type': 'layoutSettings',
|
24
|
+
block: '87911ec6-4242-4bae-b6a5-9b28151169fa',
|
25
|
+
body_class: 'body-class-1',
|
26
|
+
layout_size: 'container_view',
|
27
|
+
};
|
28
|
+
|
29
|
+
const { container } = render(
|
30
|
+
<Provider store={store}>
|
31
|
+
<Router history={history}>
|
32
|
+
<LayoutSettingsView data={data} />
|
33
|
+
</Router>
|
34
|
+
</Provider>,
|
35
|
+
);
|
36
|
+
|
37
|
+
expect(container).toBeTruthy();
|
38
|
+
});
|
39
|
+
});
|
40
|
+
|
41
|
+
describe('LayoutSettingsView Component', () => {
|
42
|
+
it('renders without crashing with multiple classes', () => {
|
43
|
+
const store = mockStore({
|
44
|
+
intl: {
|
45
|
+
locale: 'en',
|
46
|
+
messages: {},
|
47
|
+
},
|
48
|
+
});
|
49
|
+
|
50
|
+
const data = {
|
51
|
+
'@layout': 'e28ec238-4cd7-4b72-8025-66da44a6062f',
|
52
|
+
'@type': 'layoutSettings',
|
53
|
+
block: '87911ec6-4242-4bae-b6a5-9b28151169fa',
|
54
|
+
body_class: ['body-class-1', 'body-class-2'],
|
55
|
+
layout_size: 'container_view',
|
56
|
+
};
|
57
|
+
|
58
|
+
const { container } = render(
|
59
|
+
<Provider store={store}>
|
60
|
+
<Router history={history}>
|
61
|
+
<LayoutSettingsView data={data} />
|
62
|
+
</Router>
|
63
|
+
</Provider>,
|
64
|
+
);
|
65
|
+
|
66
|
+
expect(container).toBeTruthy();
|
67
|
+
});
|
68
|
+
});
|
@@ -0,0 +1,304 @@
|
|
1
|
+
/**
|
2
|
+
* CreatableSelectWidget component.
|
3
|
+
* @module components/manage/Widgets/SelectWidget
|
4
|
+
*
|
5
|
+
* A copy of the SelectWidget component. The only difference is that is uses the Creatable component as a base
|
6
|
+
*/
|
7
|
+
|
8
|
+
import React, { Component } from 'react';
|
9
|
+
import PropTypes from 'prop-types';
|
10
|
+
import { connect } from 'react-redux';
|
11
|
+
import { compose } from 'redux';
|
12
|
+
import { map } from 'lodash';
|
13
|
+
import { defineMessages, injectIntl } from 'react-intl';
|
14
|
+
import {
|
15
|
+
getVocabFromHint,
|
16
|
+
getVocabFromField,
|
17
|
+
getVocabFromItems,
|
18
|
+
} from '@plone/volto/helpers';
|
19
|
+
import { FormFieldWrapper } from '@plone/volto/components';
|
20
|
+
import { getVocabulary, getVocabularyTokenTitle } from '@plone/volto/actions';
|
21
|
+
import { normalizeValue } from '@plone/volto/components/manage/Widgets/SelectUtils';
|
22
|
+
|
23
|
+
import {
|
24
|
+
customSelectStyles,
|
25
|
+
DropdownIndicator,
|
26
|
+
ClearIndicator,
|
27
|
+
Option,
|
28
|
+
selectTheme,
|
29
|
+
MenuList,
|
30
|
+
MultiValueContainer,
|
31
|
+
} from '@plone/volto/components/manage/Widgets/SelectStyling';
|
32
|
+
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
33
|
+
|
34
|
+
import loadable from '@loadable/component';
|
35
|
+
|
36
|
+
export const Creatable = loadable(() => import('react-select/creatable'));
|
37
|
+
|
38
|
+
const messages = defineMessages({
|
39
|
+
default: {
|
40
|
+
id: 'Default',
|
41
|
+
defaultMessage: 'Default',
|
42
|
+
},
|
43
|
+
idTitle: {
|
44
|
+
id: 'Short Name',
|
45
|
+
defaultMessage: 'Short Name',
|
46
|
+
},
|
47
|
+
idDescription: {
|
48
|
+
id: 'Used for programmatic access to the fieldset.',
|
49
|
+
defaultMessage: 'Used for programmatic access to the fieldset.',
|
50
|
+
},
|
51
|
+
title: {
|
52
|
+
id: 'Title',
|
53
|
+
defaultMessage: 'Title',
|
54
|
+
},
|
55
|
+
description: {
|
56
|
+
id: 'Description',
|
57
|
+
defaultMessage: 'Description',
|
58
|
+
},
|
59
|
+
close: {
|
60
|
+
id: 'Close',
|
61
|
+
defaultMessage: 'Close',
|
62
|
+
},
|
63
|
+
choices: {
|
64
|
+
id: 'Choices',
|
65
|
+
defaultMessage: 'Choices',
|
66
|
+
},
|
67
|
+
required: {
|
68
|
+
id: 'Required',
|
69
|
+
defaultMessage: 'Required',
|
70
|
+
},
|
71
|
+
select: {
|
72
|
+
id: 'Select…',
|
73
|
+
defaultMessage: 'Select…',
|
74
|
+
},
|
75
|
+
no_value: {
|
76
|
+
id: 'No value',
|
77
|
+
defaultMessage: 'No value',
|
78
|
+
},
|
79
|
+
no_options: {
|
80
|
+
id: 'No options',
|
81
|
+
defaultMessage: 'No options',
|
82
|
+
},
|
83
|
+
});
|
84
|
+
|
85
|
+
/**
|
86
|
+
* SelectWidget component class.
|
87
|
+
* @function SelectWidget
|
88
|
+
* @returns {string} Markup of the component.
|
89
|
+
*/
|
90
|
+
class SelectWidget extends Component {
|
91
|
+
/**
|
92
|
+
* Property types.
|
93
|
+
* @property {Object} propTypes Property types.
|
94
|
+
* @static
|
95
|
+
*/
|
96
|
+
static propTypes = {
|
97
|
+
id: PropTypes.string.isRequired,
|
98
|
+
title: PropTypes.string.isRequired,
|
99
|
+
description: PropTypes.string,
|
100
|
+
required: PropTypes.bool,
|
101
|
+
error: PropTypes.arrayOf(PropTypes.string),
|
102
|
+
getVocabulary: PropTypes.func.isRequired,
|
103
|
+
getVocabularyTokenTitle: PropTypes.func.isRequired,
|
104
|
+
choices: PropTypes.arrayOf(
|
105
|
+
PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
106
|
+
),
|
107
|
+
items: PropTypes.shape({
|
108
|
+
vocabulary: PropTypes.object,
|
109
|
+
}),
|
110
|
+
widgetOptions: PropTypes.shape({
|
111
|
+
vocabulary: PropTypes.object,
|
112
|
+
}),
|
113
|
+
value: PropTypes.oneOfType([
|
114
|
+
PropTypes.object,
|
115
|
+
PropTypes.string,
|
116
|
+
PropTypes.bool,
|
117
|
+
PropTypes.func,
|
118
|
+
PropTypes.array,
|
119
|
+
]),
|
120
|
+
onChange: PropTypes.func.isRequired,
|
121
|
+
onBlur: PropTypes.func,
|
122
|
+
onClick: PropTypes.func,
|
123
|
+
onEdit: PropTypes.func,
|
124
|
+
onDelete: PropTypes.func,
|
125
|
+
wrapped: PropTypes.bool,
|
126
|
+
noValueOption: PropTypes.bool,
|
127
|
+
customOptionStyling: PropTypes.any,
|
128
|
+
isMulti: PropTypes.bool,
|
129
|
+
placeholder: PropTypes.string,
|
130
|
+
};
|
131
|
+
|
132
|
+
/**
|
133
|
+
* Default properties
|
134
|
+
* @property {Object} defaultProps Default properties.
|
135
|
+
* @static
|
136
|
+
*/
|
137
|
+
static defaultProps = {
|
138
|
+
description: null,
|
139
|
+
required: false,
|
140
|
+
items: {
|
141
|
+
vocabulary: null,
|
142
|
+
},
|
143
|
+
widgetOptions: {
|
144
|
+
vocabulary: null,
|
145
|
+
},
|
146
|
+
error: [],
|
147
|
+
choices: [],
|
148
|
+
value: null,
|
149
|
+
onChange: () => {},
|
150
|
+
onBlur: () => {},
|
151
|
+
onClick: () => {},
|
152
|
+
onEdit: null,
|
153
|
+
onDelete: null,
|
154
|
+
noValueOption: true,
|
155
|
+
customOptionStyling: null,
|
156
|
+
};
|
157
|
+
|
158
|
+
/**
|
159
|
+
* Component did mount
|
160
|
+
* @method componentDidMount
|
161
|
+
* @returns {undefined}
|
162
|
+
*/
|
163
|
+
componentDidMount() {
|
164
|
+
if (
|
165
|
+
(!this.props.choices || this.props.choices?.length === 0) &&
|
166
|
+
this.props.vocabBaseUrl
|
167
|
+
) {
|
168
|
+
this.props.getVocabulary({
|
169
|
+
vocabNameOrURL: this.props.vocabBaseUrl,
|
170
|
+
size: -1,
|
171
|
+
subrequest: this.props.lang,
|
172
|
+
});
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
componentDidUpdate(prevProps) {
|
177
|
+
if (
|
178
|
+
this.props.vocabBaseUrl !== prevProps.vocabBaseUrl &&
|
179
|
+
(!this.props.choices || this.props.choices?.length === 0)
|
180
|
+
) {
|
181
|
+
this.props.getVocabulary({
|
182
|
+
vocabNameOrURL: this.props.vocabBaseUrl,
|
183
|
+
size: -1,
|
184
|
+
subrequest: this.props.lang,
|
185
|
+
});
|
186
|
+
}
|
187
|
+
}
|
188
|
+
|
189
|
+
/**
|
190
|
+
* Render method.
|
191
|
+
* @method render
|
192
|
+
* @returns {string} Markup for the component.
|
193
|
+
*/
|
194
|
+
render() {
|
195
|
+
const { id, choices, value, intl, onChange } = this.props;
|
196
|
+
const normalizedValue = normalizeValue(choices, value, intl);
|
197
|
+
// Make sure that both disabled and isDisabled (from the DX layout feat work)
|
198
|
+
const disabled = this.props.disabled || this.props.isDisabled;
|
199
|
+
|
200
|
+
let options = this.props.vocabBaseUrl
|
201
|
+
? this.props.choices
|
202
|
+
: [
|
203
|
+
...map(choices, (option) => ({
|
204
|
+
value: option[0],
|
205
|
+
label:
|
206
|
+
// Fix "None" on the serializer, to remove when fixed in p.restapi
|
207
|
+
option[1] !== 'None' && option[1] ? option[1] : option[0],
|
208
|
+
})),
|
209
|
+
// Only set "no-value" option if there's no default in the field
|
210
|
+
// TODO: also if this.props.defaultValue?
|
211
|
+
...(this.props.noValueOption &&
|
212
|
+
(this.props.default === undefined || this.props.default === null)
|
213
|
+
? [
|
214
|
+
{
|
215
|
+
label: this.props.intl.formatMessage(messages.no_value),
|
216
|
+
value: 'no-value',
|
217
|
+
},
|
218
|
+
]
|
219
|
+
: []),
|
220
|
+
];
|
221
|
+
|
222
|
+
return (
|
223
|
+
<FormFieldWrapper {...this.props}>
|
224
|
+
<Creatable
|
225
|
+
isMulti
|
226
|
+
id={`field-${id}`}
|
227
|
+
key={choices}
|
228
|
+
name={id}
|
229
|
+
menuShouldScrollIntoView={false}
|
230
|
+
isDisabled={disabled}
|
231
|
+
isSearchable={true}
|
232
|
+
className="react-select-container"
|
233
|
+
classNamePrefix="react-select"
|
234
|
+
options={options}
|
235
|
+
styles={customSelectStyles}
|
236
|
+
theme={selectTheme}
|
237
|
+
components={{
|
238
|
+
...(options?.length > 25 && {
|
239
|
+
MenuList,
|
240
|
+
}),
|
241
|
+
MultiValueContainer,
|
242
|
+
DropdownIndicator,
|
243
|
+
ClearIndicator,
|
244
|
+
Option: this.props.customOptionStyling || Option,
|
245
|
+
}}
|
246
|
+
value={normalizedValue}
|
247
|
+
placeholder={
|
248
|
+
this.props.placeholder ??
|
249
|
+
this.props.intl.formatMessage(messages.select)
|
250
|
+
}
|
251
|
+
onChange={(selectedOption) => {
|
252
|
+
return onChange(
|
253
|
+
id,
|
254
|
+
selectedOption.map((el) => el.value),
|
255
|
+
);
|
256
|
+
}}
|
257
|
+
isClearable
|
258
|
+
/>
|
259
|
+
</FormFieldWrapper>
|
260
|
+
);
|
261
|
+
}
|
262
|
+
}
|
263
|
+
|
264
|
+
export const SelectWidgetComponent = injectIntl(SelectWidget);
|
265
|
+
|
266
|
+
export default compose(
|
267
|
+
injectLazyLibs(['reactSelect']),
|
268
|
+
connect(
|
269
|
+
(state, props) => {
|
270
|
+
const vocabBaseUrl = !props.choices
|
271
|
+
? getVocabFromHint(props) ||
|
272
|
+
getVocabFromField(props) ||
|
273
|
+
getVocabFromItems(props)
|
274
|
+
: '';
|
275
|
+
|
276
|
+
const vocabState =
|
277
|
+
state.vocabularies?.[vocabBaseUrl]?.subrequests?.[state.intl.locale];
|
278
|
+
|
279
|
+
// If the schema already has the choices in it, then do not try to get the vocab,
|
280
|
+
// even if there is one
|
281
|
+
if (props.choices) {
|
282
|
+
return {
|
283
|
+
choices: props.choices,
|
284
|
+
lang: state.intl.locale,
|
285
|
+
};
|
286
|
+
} else if (vocabState) {
|
287
|
+
return {
|
288
|
+
vocabBaseUrl,
|
289
|
+
choices: vocabState?.items ?? [],
|
290
|
+
lang: state.intl.locale,
|
291
|
+
};
|
292
|
+
// There is a moment that vocabState is not there yet, so we need to pass the
|
293
|
+
// vocabBaseUrl to the component.
|
294
|
+
} else if (vocabBaseUrl) {
|
295
|
+
return {
|
296
|
+
vocabBaseUrl,
|
297
|
+
lang: state.intl.locale,
|
298
|
+
};
|
299
|
+
}
|
300
|
+
return { lang: state.intl.locale };
|
301
|
+
},
|
302
|
+
{ getVocabulary, getVocabularyTokenTitle },
|
303
|
+
),
|
304
|
+
)(SelectWidgetComponent);
|
@@ -0,0 +1,89 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import configureStore from 'redux-mock-store';
|
3
|
+
import { Provider } from 'react-intl-redux';
|
4
|
+
import { waitFor, render, screen, fireEvent } from '@testing-library/react';
|
5
|
+
|
6
|
+
import CreatableSelectWidget from './CreatableSelectWidget';
|
7
|
+
|
8
|
+
const mockStore = configureStore();
|
9
|
+
|
10
|
+
jest.mock('@plone/volto/helpers/Loadable/Loadable');
|
11
|
+
beforeAll(
|
12
|
+
async () =>
|
13
|
+
await require('@plone/volto/helpers/Loadable/Loadable').__setLoadables(),
|
14
|
+
);
|
15
|
+
|
16
|
+
test('renders a select widget component', async () => {
|
17
|
+
const store = mockStore({
|
18
|
+
intl: {
|
19
|
+
locale: 'en',
|
20
|
+
messages: {},
|
21
|
+
},
|
22
|
+
vocabularies: {
|
23
|
+
'plone.app.vocabularies.Keywords': {
|
24
|
+
items: [{ title: 'My item', value: 'myitem' }],
|
25
|
+
itemsTotal: 1,
|
26
|
+
},
|
27
|
+
},
|
28
|
+
});
|
29
|
+
|
30
|
+
const { container } = render(
|
31
|
+
<Provider store={store}>
|
32
|
+
<CreatableSelectWidget
|
33
|
+
id="my-field"
|
34
|
+
title="My field"
|
35
|
+
fieldSet="default"
|
36
|
+
onChange={() => {}}
|
37
|
+
onBlur={() => {}}
|
38
|
+
onClick={() => {}}
|
39
|
+
/>
|
40
|
+
</Provider>,
|
41
|
+
);
|
42
|
+
|
43
|
+
await waitFor(() => screen.getByText('My field'));
|
44
|
+
expect(container).toBeTruthy();
|
45
|
+
});
|
46
|
+
|
47
|
+
test("No 'No value' option when default value is 0", async () => {
|
48
|
+
const store = mockStore({
|
49
|
+
intl: {
|
50
|
+
locale: 'en',
|
51
|
+
messages: {},
|
52
|
+
},
|
53
|
+
});
|
54
|
+
|
55
|
+
const choices = [
|
56
|
+
['0', 'None'],
|
57
|
+
['1', 'One'],
|
58
|
+
];
|
59
|
+
|
60
|
+
const value = {
|
61
|
+
value: '0',
|
62
|
+
label: 'None',
|
63
|
+
};
|
64
|
+
|
65
|
+
const _default = 0;
|
66
|
+
|
67
|
+
const { container } = render(
|
68
|
+
<Provider store={store}>
|
69
|
+
<CreatableSelectWidget
|
70
|
+
id="my-field"
|
71
|
+
title="My field"
|
72
|
+
fieldSet="default"
|
73
|
+
choices={choices}
|
74
|
+
default={_default}
|
75
|
+
value={value}
|
76
|
+
onChange={() => {}}
|
77
|
+
onBlur={() => {}}
|
78
|
+
onClick={() => {}}
|
79
|
+
/>
|
80
|
+
</Provider>,
|
81
|
+
);
|
82
|
+
|
83
|
+
await waitFor(() => screen.getByText('None'));
|
84
|
+
fireEvent.mouseDown(
|
85
|
+
container.querySelector('.react-select__dropdown-indicator'),
|
86
|
+
{ button: 0 },
|
87
|
+
);
|
88
|
+
expect(container).toBeTruthy();
|
89
|
+
});
|
@@ -0,0 +1,498 @@
|
|
1
|
+
/**
|
2
|
+
* Add container.
|
3
|
+
* @module components/manage/Add/Add
|
4
|
+
*/
|
5
|
+
|
6
|
+
import React, { Component } from 'react';
|
7
|
+
import PropTypes from 'prop-types';
|
8
|
+
import { BodyClass, Helmet } from '@plone/volto/helpers';
|
9
|
+
import { connect } from 'react-redux';
|
10
|
+
import { compose } from 'redux';
|
11
|
+
import { keys, isEmpty } from 'lodash';
|
12
|
+
import { defineMessages, injectIntl } from 'react-intl';
|
13
|
+
import { Button, Grid, Menu } from 'semantic-ui-react';
|
14
|
+
import { Portal } from 'react-portal';
|
15
|
+
import { v4 as uuid } from 'uuid';
|
16
|
+
import qs from 'query-string';
|
17
|
+
import { toast } from 'react-toastify';
|
18
|
+
|
19
|
+
import { createContent, getSchema, changeLanguage } from '@plone/volto/actions';
|
20
|
+
import {
|
21
|
+
Form,
|
22
|
+
Icon,
|
23
|
+
Toolbar,
|
24
|
+
Sidebar,
|
25
|
+
Toast,
|
26
|
+
TranslationObject,
|
27
|
+
} from '@plone/volto/components';
|
28
|
+
import {
|
29
|
+
getBaseUrl,
|
30
|
+
hasBlocksData,
|
31
|
+
flattenToAppURL,
|
32
|
+
getBlocksFieldname,
|
33
|
+
getBlocksLayoutFieldname,
|
34
|
+
getLanguageIndependentFields,
|
35
|
+
langmap,
|
36
|
+
toGettextLang,
|
37
|
+
} from '@plone/volto/helpers';
|
38
|
+
|
39
|
+
import { preloadLazyLibs } from '@plone/volto/helpers/Loadable';
|
40
|
+
import { tryParseJSON } from '@plone/volto/helpers';
|
41
|
+
|
42
|
+
import config from '@plone/volto/registry';
|
43
|
+
|
44
|
+
import saveSVG from '@plone/volto/icons/save.svg';
|
45
|
+
import clearSVG from '@plone/volto/icons/clear.svg';
|
46
|
+
|
47
|
+
const messages = defineMessages({
|
48
|
+
add: {
|
49
|
+
id: 'Add {type}',
|
50
|
+
defaultMessage: 'Add {type}',
|
51
|
+
},
|
52
|
+
save: {
|
53
|
+
id: 'Save',
|
54
|
+
defaultMessage: 'Save',
|
55
|
+
},
|
56
|
+
cancel: {
|
57
|
+
id: 'Cancel',
|
58
|
+
defaultMessage: 'Cancel',
|
59
|
+
},
|
60
|
+
error: {
|
61
|
+
id: 'Error',
|
62
|
+
defaultMessage: 'Error',
|
63
|
+
},
|
64
|
+
translateTo: {
|
65
|
+
id: 'Translate to {lang}',
|
66
|
+
defaultMessage: 'Translate to {lang}',
|
67
|
+
},
|
68
|
+
someErrors: {
|
69
|
+
id: 'There are some errors.',
|
70
|
+
defaultMessage: 'There are some errors.',
|
71
|
+
},
|
72
|
+
});
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Add class.
|
76
|
+
* @class Add
|
77
|
+
* @extends Component
|
78
|
+
*/
|
79
|
+
class Add extends Component {
|
80
|
+
/**
|
81
|
+
* Property types.
|
82
|
+
* @property {Object} propTypes Property types.
|
83
|
+
* @static
|
84
|
+
*/
|
85
|
+
static propTypes = {
|
86
|
+
createContent: PropTypes.func.isRequired,
|
87
|
+
getSchema: PropTypes.func.isRequired,
|
88
|
+
pathname: PropTypes.string.isRequired,
|
89
|
+
schema: PropTypes.objectOf(PropTypes.any),
|
90
|
+
content: PropTypes.shape({
|
91
|
+
// eslint-disable-line react/no-unused-prop-types
|
92
|
+
'@id': PropTypes.string,
|
93
|
+
'@type': PropTypes.string,
|
94
|
+
}),
|
95
|
+
returnUrl: PropTypes.string,
|
96
|
+
createRequest: PropTypes.shape({
|
97
|
+
loading: PropTypes.bool,
|
98
|
+
loaded: PropTypes.bool,
|
99
|
+
}).isRequired,
|
100
|
+
schemaRequest: PropTypes.shape({
|
101
|
+
loading: PropTypes.bool,
|
102
|
+
loaded: PropTypes.bool,
|
103
|
+
}).isRequired,
|
104
|
+
type: PropTypes.string,
|
105
|
+
location: PropTypes.objectOf(PropTypes.any),
|
106
|
+
};
|
107
|
+
|
108
|
+
/**
|
109
|
+
* Default properties
|
110
|
+
* @property {Object} defaultProps Default properties.
|
111
|
+
* @static
|
112
|
+
*/
|
113
|
+
static defaultProps = {
|
114
|
+
schema: null,
|
115
|
+
content: null,
|
116
|
+
returnUrl: null,
|
117
|
+
type: 'Default',
|
118
|
+
};
|
119
|
+
|
120
|
+
/**
|
121
|
+
* Constructor
|
122
|
+
* @method constructor
|
123
|
+
* @param {Object} props Component properties
|
124
|
+
* @constructs WysiwygEditor
|
125
|
+
*/
|
126
|
+
constructor(props) {
|
127
|
+
super(props);
|
128
|
+
this.onCancel = this.onCancel.bind(this);
|
129
|
+
this.onSubmit = this.onSubmit.bind(this);
|
130
|
+
|
131
|
+
if (config.blocks?.initialBlocks[props.type]) {
|
132
|
+
this.initialBlocksLayout = config.blocks.initialBlocks[
|
133
|
+
props.type
|
134
|
+
].map((item) => uuid());
|
135
|
+
this.initialBlocks = this.initialBlocksLayout.reduce(
|
136
|
+
(acc, value, index) => ({
|
137
|
+
...acc,
|
138
|
+
[value]: { '@type': config.blocks.initialBlocks[props.type][index] },
|
139
|
+
}),
|
140
|
+
{},
|
141
|
+
);
|
142
|
+
}
|
143
|
+
this.state = {
|
144
|
+
isClient: false,
|
145
|
+
error: null,
|
146
|
+
formSelected: 'addForm',
|
147
|
+
};
|
148
|
+
}
|
149
|
+
|
150
|
+
/**
|
151
|
+
* Component did mount
|
152
|
+
* @method componentDidMount
|
153
|
+
* @returns {undefined}
|
154
|
+
*/
|
155
|
+
componentDidMount() {
|
156
|
+
this.props.getSchema(this.props.type, getBaseUrl(this.props.pathname));
|
157
|
+
this.setState({ isClient: true });
|
158
|
+
}
|
159
|
+
|
160
|
+
/**
|
161
|
+
* Component will receive props
|
162
|
+
* @method componentWillReceiveProps
|
163
|
+
* @param {Object} nextProps Next properties
|
164
|
+
* @returns {undefined}
|
165
|
+
*/
|
166
|
+
UNSAFE_componentWillReceiveProps(nextProps) {
|
167
|
+
if (
|
168
|
+
this.props.createRequest.loading &&
|
169
|
+
nextProps.createRequest.loaded &&
|
170
|
+
nextProps.content['@type'] === this.props.type
|
171
|
+
) {
|
172
|
+
this.props.history.push(
|
173
|
+
this.props.returnUrl || flattenToAppURL(nextProps.content['@id']),
|
174
|
+
);
|
175
|
+
}
|
176
|
+
|
177
|
+
if (this.props.createRequest.loading && nextProps.createRequest.error) {
|
178
|
+
const message =
|
179
|
+
nextProps.createRequest.error.response?.body?.message ||
|
180
|
+
nextProps.createRequest.error.response?.text;
|
181
|
+
|
182
|
+
const error =
|
183
|
+
new DOMParser().parseFromString(message, 'text/html')?.all[0]
|
184
|
+
?.textContent || message;
|
185
|
+
|
186
|
+
const errorsList = tryParseJSON(error);
|
187
|
+
let erroMessage;
|
188
|
+
if (Array.isArray(errorsList)) {
|
189
|
+
const invariantErrors = errorsList
|
190
|
+
.filter((errorItem) => !('field' in errorItem))
|
191
|
+
.map((errorItem) => errorItem['message']);
|
192
|
+
if (invariantErrors.length > 0) {
|
193
|
+
// Plone invariant validation message.
|
194
|
+
erroMessage = invariantErrors.join(' - ');
|
195
|
+
} else {
|
196
|
+
// Error in specific field.
|
197
|
+
erroMessage = this.props.intl.formatMessage(messages.someErrors);
|
198
|
+
}
|
199
|
+
} else {
|
200
|
+
erroMessage = errorsList.error?.message || error;
|
201
|
+
}
|
202
|
+
|
203
|
+
this.setState({ error: error });
|
204
|
+
|
205
|
+
if (
|
206
|
+
document?.querySelector('.sidebar-container .tabs-wrapper a')?.click
|
207
|
+
) {
|
208
|
+
document.querySelector('.sidebar-container .tabs-wrapper a').click();
|
209
|
+
}
|
210
|
+
toast.error(
|
211
|
+
<Toast
|
212
|
+
error
|
213
|
+
title={this.props.intl.formatMessage(messages.error)}
|
214
|
+
content={erroMessage}
|
215
|
+
/>,
|
216
|
+
);
|
217
|
+
}
|
218
|
+
}
|
219
|
+
|
220
|
+
/**
|
221
|
+
* Submit handler
|
222
|
+
* @method onSubmit
|
223
|
+
* @param {object} data Form data.
|
224
|
+
* @returns {undefined}
|
225
|
+
*/
|
226
|
+
onSubmit(data) {
|
227
|
+
this.props.createContent(getBaseUrl(this.props.pathname), {
|
228
|
+
...data,
|
229
|
+
'@static_behaviors': this.props.schema.definitions
|
230
|
+
? keys(this.props.schema.definitions)
|
231
|
+
: null,
|
232
|
+
'@type': this.props.type,
|
233
|
+
...(config.settings.isMultilingual &&
|
234
|
+
this.props.location?.state?.translationOf && {
|
235
|
+
translation_of: this.props.location.state.translationOf,
|
236
|
+
language: this.props.location.state.language,
|
237
|
+
}),
|
238
|
+
});
|
239
|
+
}
|
240
|
+
|
241
|
+
/**
|
242
|
+
* Cancel handler
|
243
|
+
* @method onCancel
|
244
|
+
* @returns {undefined}
|
245
|
+
*/
|
246
|
+
onCancel() {
|
247
|
+
if (this.props.location?.state?.translationOf) {
|
248
|
+
const language = this.props.location.state.languageFrom;
|
249
|
+
const langFileName = toGettextLang(language);
|
250
|
+
import('@root/../locales/' + langFileName + '.json').then((locale) => {
|
251
|
+
this.props.changeLanguage(language, locale.default);
|
252
|
+
});
|
253
|
+
this.props.history.push(this.props.location?.state?.translationOf);
|
254
|
+
} else {
|
255
|
+
this.props.history.push(getBaseUrl(this.props.pathname));
|
256
|
+
}
|
257
|
+
}
|
258
|
+
|
259
|
+
form = React.createRef();
|
260
|
+
|
261
|
+
/**
|
262
|
+
* Render method.
|
263
|
+
* @method render
|
264
|
+
* @returns {string} Markup for the component.
|
265
|
+
*/
|
266
|
+
render() {
|
267
|
+
if (this.props.schemaRequest.loaded) {
|
268
|
+
const visual = hasBlocksData(this.props.schema.properties);
|
269
|
+
const blocksFieldname = getBlocksFieldname(this.props.schema.properties);
|
270
|
+
const blocksLayoutFieldname = getBlocksLayoutFieldname(
|
271
|
+
this.props.schema.properties,
|
272
|
+
);
|
273
|
+
const translationObject = this.props.location?.state?.translationObject;
|
274
|
+
|
275
|
+
const translateTo = translationObject
|
276
|
+
? langmap?.[this.props.location?.state?.language]?.nativeName
|
277
|
+
: null;
|
278
|
+
|
279
|
+
// Lookup initialBlocks and initialBlocksLayout within schema
|
280
|
+
const schemaBlocks = this.props.schema.properties[blocksFieldname]
|
281
|
+
?.default;
|
282
|
+
const schemaBlocksLayout = this.props.schema.properties[
|
283
|
+
blocksLayoutFieldname
|
284
|
+
]?.default?.items;
|
285
|
+
let initialBlocks = this.initialBlocks;
|
286
|
+
let initialBlocksLayout = this.initialBlocksLayout;
|
287
|
+
|
288
|
+
if (!isEmpty(schemaBlocksLayout) && !isEmpty(schemaBlocks)) {
|
289
|
+
initialBlocks = {};
|
290
|
+
initialBlocksLayout = [];
|
291
|
+
schemaBlocksLayout.forEach((value) => {
|
292
|
+
if (!isEmpty(schemaBlocks[value])) {
|
293
|
+
let newUid = uuid();
|
294
|
+
initialBlocksLayout.push(newUid);
|
295
|
+
initialBlocks[newUid] = schemaBlocks[value];
|
296
|
+
initialBlocks[newUid].block = newUid;
|
297
|
+
|
298
|
+
// Layout ID - keep a reference to the original block id within layout
|
299
|
+
initialBlocks[newUid]['@layout'] = value;
|
300
|
+
}
|
301
|
+
});
|
302
|
+
}
|
303
|
+
//copy blocks from translationObject
|
304
|
+
if (translationObject && blocksFieldname && blocksLayoutFieldname) {
|
305
|
+
initialBlocks = {};
|
306
|
+
initialBlocksLayout = [];
|
307
|
+
const originalBlocks = JSON.parse(
|
308
|
+
JSON.stringify(translationObject[blocksFieldname]),
|
309
|
+
);
|
310
|
+
const originalBlocksLayout =
|
311
|
+
translationObject[blocksLayoutFieldname].items;
|
312
|
+
|
313
|
+
originalBlocksLayout.forEach((value) => {
|
314
|
+
if (!isEmpty(originalBlocks[value])) {
|
315
|
+
let newUid = uuid();
|
316
|
+
initialBlocksLayout.push(newUid);
|
317
|
+
initialBlocks[newUid] = originalBlocks[value];
|
318
|
+
initialBlocks[newUid].block = newUid;
|
319
|
+
|
320
|
+
// Layout ID - keep a reference to the original block id within layout
|
321
|
+
initialBlocks[newUid]['@canonical'] = value;
|
322
|
+
}
|
323
|
+
});
|
324
|
+
}
|
325
|
+
|
326
|
+
const lifData = () => {
|
327
|
+
const data = {};
|
328
|
+
if (translationObject) {
|
329
|
+
getLanguageIndependentFields(this.props.schema).forEach(
|
330
|
+
(lif) => (data[lif] = translationObject[lif]),
|
331
|
+
);
|
332
|
+
}
|
333
|
+
return data;
|
334
|
+
};
|
335
|
+
|
336
|
+
const pageAdd = (
|
337
|
+
<div id="page-add">
|
338
|
+
<Helmet
|
339
|
+
title={this.props.intl.formatMessage(messages.add, {
|
340
|
+
type: this.props.type,
|
341
|
+
})}
|
342
|
+
/>
|
343
|
+
<Form
|
344
|
+
ref={this.form}
|
345
|
+
key="translated-or-new-content-form"
|
346
|
+
navRoot={
|
347
|
+
this.props.content?.['@components']?.navroot?.navroot || {}
|
348
|
+
}
|
349
|
+
schema={this.props.schema}
|
350
|
+
type={this.props.type}
|
351
|
+
formData={{
|
352
|
+
...(blocksFieldname && {
|
353
|
+
[blocksFieldname]:
|
354
|
+
initialBlocks ||
|
355
|
+
this.props.schema.properties[blocksFieldname]?.default,
|
356
|
+
}),
|
357
|
+
...(blocksLayoutFieldname && {
|
358
|
+
[blocksLayoutFieldname]: {
|
359
|
+
items:
|
360
|
+
initialBlocksLayout ||
|
361
|
+
this.props.schema.properties[blocksLayoutFieldname]?.default
|
362
|
+
?.items,
|
363
|
+
},
|
364
|
+
}),
|
365
|
+
// Copy the Language Independent Fields values from the to-be translated content
|
366
|
+
// into the default values of the translated content Add form.
|
367
|
+
...lifData(),
|
368
|
+
parent: {
|
369
|
+
'@id': this.props.content?.['@id'] || '',
|
370
|
+
},
|
371
|
+
}}
|
372
|
+
requestError={this.state.error}
|
373
|
+
onSubmit={this.onSubmit}
|
374
|
+
hideActions
|
375
|
+
pathname={this.props.pathname}
|
376
|
+
visual={visual}
|
377
|
+
title={
|
378
|
+
this.props?.schema?.title
|
379
|
+
? this.props.intl.formatMessage(messages.add, {
|
380
|
+
type: this.props.schema.title,
|
381
|
+
})
|
382
|
+
: null
|
383
|
+
}
|
384
|
+
loading={this.props.createRequest.loading}
|
385
|
+
isFormSelected={this.state.formSelected === 'addForm'}
|
386
|
+
onSelectForm={() => {
|
387
|
+
this.setState({ formSelected: 'addForm' });
|
388
|
+
}}
|
389
|
+
global
|
390
|
+
/>
|
391
|
+
{this.state.isClient && (
|
392
|
+
<Portal node={document.getElementById('toolbar')}>
|
393
|
+
<Toolbar
|
394
|
+
pathname={this.props.pathname}
|
395
|
+
hideDefaultViewButtons
|
396
|
+
inner={
|
397
|
+
<>
|
398
|
+
<Button
|
399
|
+
id="toolbar-save"
|
400
|
+
className="save"
|
401
|
+
aria-label={this.props.intl.formatMessage(messages.save)}
|
402
|
+
onClick={() => this.form.current.onSubmit()}
|
403
|
+
loading={this.props.createRequest.loading}
|
404
|
+
>
|
405
|
+
<Icon
|
406
|
+
name={saveSVG}
|
407
|
+
className="circled"
|
408
|
+
size="30px"
|
409
|
+
title={this.props.intl.formatMessage(messages.save)}
|
410
|
+
/>
|
411
|
+
</Button>
|
412
|
+
<Button className="cancel" onClick={() => this.onCancel()}>
|
413
|
+
<Icon
|
414
|
+
name={clearSVG}
|
415
|
+
className="circled"
|
416
|
+
aria-label={this.props.intl.formatMessage(
|
417
|
+
messages.cancel,
|
418
|
+
)}
|
419
|
+
size="30px"
|
420
|
+
title={this.props.intl.formatMessage(messages.cancel)}
|
421
|
+
/>
|
422
|
+
</Button>
|
423
|
+
</>
|
424
|
+
}
|
425
|
+
/>
|
426
|
+
</Portal>
|
427
|
+
)}
|
428
|
+
{visual && this.state.isClient && (
|
429
|
+
<Portal node={document.getElementById('sidebar')}>
|
430
|
+
<Sidebar />
|
431
|
+
</Portal>
|
432
|
+
)}
|
433
|
+
</div>
|
434
|
+
);
|
435
|
+
|
436
|
+
return translationObject ? (
|
437
|
+
<>
|
438
|
+
<BodyClass className="babel-view" />
|
439
|
+
<Grid
|
440
|
+
celled="internally"
|
441
|
+
stackable
|
442
|
+
columns={2}
|
443
|
+
id="page-add-translation"
|
444
|
+
>
|
445
|
+
<Grid.Column className="source-object">
|
446
|
+
<TranslationObject
|
447
|
+
translationObject={translationObject}
|
448
|
+
schema={this.props.schema}
|
449
|
+
pathname={this.props.pathname}
|
450
|
+
visual={visual}
|
451
|
+
isFormSelected={
|
452
|
+
this.state.formSelected === 'translationObjectForm'
|
453
|
+
}
|
454
|
+
onSelectForm={() => {
|
455
|
+
this.setState({
|
456
|
+
formSelected: 'translationObjectForm',
|
457
|
+
});
|
458
|
+
}}
|
459
|
+
/>
|
460
|
+
</Grid.Column>
|
461
|
+
<Grid.Column>
|
462
|
+
<div className="new-translation">
|
463
|
+
<Menu pointing secondary attached tabular>
|
464
|
+
<Menu.Item name={translateTo.toUpperCase()} active={true}>
|
465
|
+
{`${this.props.intl.formatMessage(messages.translateTo, {
|
466
|
+
lang: translateTo,
|
467
|
+
})}`}
|
468
|
+
</Menu.Item>
|
469
|
+
</Menu>
|
470
|
+
{pageAdd}
|
471
|
+
</div>
|
472
|
+
</Grid.Column>
|
473
|
+
</Grid>
|
474
|
+
</>
|
475
|
+
) : (
|
476
|
+
pageAdd
|
477
|
+
);
|
478
|
+
}
|
479
|
+
return <div />;
|
480
|
+
}
|
481
|
+
}
|
482
|
+
|
483
|
+
export default compose(
|
484
|
+
injectIntl,
|
485
|
+
connect(
|
486
|
+
(state, props) => ({
|
487
|
+
createRequest: state.content.create,
|
488
|
+
schemaRequest: state.schema,
|
489
|
+
content: state.content.data,
|
490
|
+
schema: state.schema.schema,
|
491
|
+
pathname: props.location.pathname,
|
492
|
+
returnUrl: qs.parse(props.location.search).return_url,
|
493
|
+
type: qs.parse(props.location.search).type,
|
494
|
+
}),
|
495
|
+
{ createContent, getSchema, changeLanguage },
|
496
|
+
),
|
497
|
+
preloadLazyLibs('cms'),
|
498
|
+
)(Add);
|
@@ -0,0 +1 @@
|
|
1
|
+
This customization aims to resolve the issue of adding a page with an empty title. It is a temporary solution and should be removed upon upgrading to Volto 17. Please check this for more information: https://github.com/plone/volto/pull/5842
|
@@ -20,7 +20,7 @@ import { Copyright } from '@eeacms/volto-eea-design-system/ui';
|
|
20
20
|
*/
|
21
21
|
export const View = (props) => {
|
22
22
|
const { data, detached } = props;
|
23
|
-
const href = data?.href?.[0]?.['@id']
|
23
|
+
const href = data?.href?.[0]?.['@id'] || '';
|
24
24
|
const { copyright, copyrightIcon, copyrightPosition } = data;
|
25
25
|
// const [hovering, setHovering] = React.useState(false);
|
26
26
|
const [viewLoaded, setViewLoaded] = React.useState(false);
|
@@ -66,7 +66,13 @@ export const getVoltoStyles = (props) => {
|
|
66
66
|
if (styles[key] === true) {
|
67
67
|
output[key] = key;
|
68
68
|
} else {
|
69
|
-
|
69
|
+
if (Array.isArray(value)) {
|
70
|
+
value.forEach((el, i) => {
|
71
|
+
output[el] = el;
|
72
|
+
});
|
73
|
+
} else {
|
74
|
+
output[value] = value;
|
75
|
+
}
|
70
76
|
}
|
71
77
|
}
|
72
78
|
return output;
|
package/src/index.js
CHANGED
@@ -6,8 +6,9 @@ import HomePageView from '@eeacms/volto-eea-website-theme/components/theme/Homep
|
|
6
6
|
import NotFound from '@eeacms/volto-eea-website-theme/components/theme/NotFound/NotFound';
|
7
7
|
import { TokenWidget } from '@eeacms/volto-eea-website-theme/components/theme/Widgets/TokenWidget';
|
8
8
|
import { TopicsWidget } from '@eeacms/volto-eea-website-theme/components/theme/Widgets/TopicsWidget';
|
9
|
+
import CreatableSelectWidget from '@eeacms/volto-eea-website-theme/components/theme/Widgets/CreatableSelectWidget';
|
10
|
+
|
9
11
|
import { Icon } from '@plone/volto/components';
|
10
|
-
import { getBlocks } from '@plone/volto/helpers';
|
11
12
|
import { serializeNodesToText } from '@plone/volto-slate/editor/render';
|
12
13
|
import Tag from '@eeacms/volto-eea-design-system/ui/Tag/Tag';
|
13
14
|
|
@@ -301,26 +302,6 @@ const applyConfig = (config) => {
|
|
301
302
|
// Apply columns block customization
|
302
303
|
if (config.blocks.blocksConfig.columnsBlock) {
|
303
304
|
config.blocks.blocksConfig.columnsBlock.available_colors = eea.colors;
|
304
|
-
config.blocks.blocksConfig.columnsBlock.tocEntries = (
|
305
|
-
tocData,
|
306
|
-
block = {},
|
307
|
-
) => {
|
308
|
-
// integration with volto-block-toc
|
309
|
-
const headlines = tocData.levels || ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
|
310
|
-
let entries = [];
|
311
|
-
const sorted_column_blocks = getBlocks(block?.data || {});
|
312
|
-
sorted_column_blocks.forEach((column_block) => {
|
313
|
-
const sorted_blocks = getBlocks(column_block[1]);
|
314
|
-
sorted_blocks.forEach((block) => {
|
315
|
-
const { value, plaintext } = block[1];
|
316
|
-
const type = value?.[0]?.type;
|
317
|
-
if (headlines.includes(type)) {
|
318
|
-
entries.push([parseInt(type.slice(1)), plaintext, block[0]]);
|
319
|
-
}
|
320
|
-
});
|
321
|
-
});
|
322
|
-
return entries;
|
323
|
-
};
|
324
305
|
}
|
325
306
|
|
326
307
|
// Description block custom CSS
|
@@ -336,6 +317,7 @@ const applyConfig = (config) => {
|
|
336
317
|
config.widgets.views.id.topics = TopicsWidget;
|
337
318
|
config.widgets.views.id.subjects = TokenWidget;
|
338
319
|
config.widgets.views.widget.tags = TokenWidget;
|
320
|
+
config.widgets.widget.creatable_select = CreatableSelectWidget;
|
339
321
|
|
340
322
|
// /voltoCustom.css express-middleware
|
341
323
|
// /ok express-middleware - see also: https://github.com/plone/volto/pull/4432
|
package/src/index.test.js
CHANGED
@@ -91,6 +91,7 @@ describe('applyConfig', () => {
|
|
91
91
|
tags: undefined,
|
92
92
|
},
|
93
93
|
},
|
94
|
+
widget: {},
|
94
95
|
},
|
95
96
|
settings: {
|
96
97
|
eea: {
|
@@ -243,6 +244,7 @@ describe('applyConfig', () => {
|
|
243
244
|
tags: undefined,
|
244
245
|
},
|
245
246
|
},
|
247
|
+
widget: {},
|
246
248
|
},
|
247
249
|
settings: {
|
248
250
|
eea: {},
|