@eeacms/volto-clms-theme 1.0.156 → 1.0.158
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 +31 -2
- package/Jenkinsfile +9 -6
- package/package.json +3 -2
- package/src/components/Blocks/CclFAQBlock/CclFAQBlockEdit.jsx +8 -0
- package/src/components/Blocks/CclFAQBlock/CclFAQBlockView.jsx +149 -0
- package/src/components/Blocks/customBlocks.js +18 -0
- package/src/components/Blocks/utils.js +5 -3
- package/src/components/CLMSDatasetDetailView/CLMSDatasetDetailView.jsx +7 -1
- package/src/components/CLMSDownloadCartView/CLMSCartContent.jsx +10 -4
- package/src/components/CclTab/CclTab.jsx +8 -4
- package/src/components/CclTab/CclTabs.jsx +14 -7
- package/src/customizations/volto/components/manage/Diff/DiffField.jsx +195 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,11 +4,40 @@ 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.0.
|
|
7
|
+
### [1.0.158](https://github.com/eea/volto-clms-theme/compare/1.0.157...1.0.158) - 22 December 2022
|
|
8
|
+
|
|
9
|
+
#### :bug: Bug Fixes
|
|
10
|
+
|
|
11
|
+
- fix: add b_size to the FAQ search request and remove duplicates from categories [ionlizarazu - [`94c0b19`](https://github.com/eea/volto-clms-theme/commit/94c0b19b9c990e675125b45522d15d896ee53792)]
|
|
12
|
+
|
|
13
|
+
### [1.0.157](https://github.com/eea/volto-clms-theme/compare/1.0.156...1.0.157) - 22 December 2022
|
|
8
14
|
|
|
9
15
|
#### :rocket: New Features
|
|
10
16
|
|
|
11
|
-
- feat:
|
|
17
|
+
- feat: copy to customize [Mikel Larreategi - [`84bb8d6`](https://github.com/eea/volto-clms-theme/commit/84bb8d6de43acccf70dd009088f91a09168259af)]
|
|
18
|
+
- feat: use volto-addon-ci from 15.x tag [Mikel Larreategi - [`f21deaa`](https://github.com/eea/volto-clms-theme/commit/f21deaaf20b8873886f10b05c42470c9e12d0686)]
|
|
19
|
+
- feat: do not show View in the mapviewer link if this item will not bee shown [Mikel Larreategi - [`fbda05f`](https://github.com/eea/volto-clms-theme/commit/fbda05f25c62e5dd9a4a66a1bdc53170e93ca0a2)]
|
|
20
|
+
- feat: show dataset names in the download pop-up when a custom selection and prepackaged are requested to be downloaded [Mikel Larreategi - [`cdb2564`](https://github.com/eea/volto-clms-theme/commit/cdb25648d6ae44d5c0892a4ea3cddb5c03b04e4c)]
|
|
21
|
+
- feat: FAQ Block [Mikel Larreategi - [`8ffabf7`](https://github.com/eea/volto-clms-theme/commit/8ffabf7c1f1353020a40b3f2c14ee979872ed607)]
|
|
22
|
+
|
|
23
|
+
#### :bug: Bug Fixes
|
|
24
|
+
|
|
25
|
+
- fix: add import/no-resolver ignore [Mikel Larreategi - [`ba527ba`](https://github.com/eea/volto-clms-theme/commit/ba527ba3bbedbcf44a70ef68ee641d5bc97d50f3)]
|
|
26
|
+
- fix: use Grid instead of Table [Mikel Larreategi - [`4d9bed0`](https://github.com/eea/volto-clms-theme/commit/4d9bed05301b6e4361e0ab27ae5650e44f703cde)]
|
|
27
|
+
|
|
28
|
+
#### :house: Internal changes
|
|
29
|
+
|
|
30
|
+
- chore: fix [Mikel Larreategi - [`c12a7c3`](https://github.com/eea/volto-clms-theme/commit/c12a7c3aaae93a37776de1eefadda523941de82f)]
|
|
31
|
+
- chore: fix [Mikel Larreategi - [`3ac089a`](https://github.com/eea/volto-clms-theme/commit/3ac089a9ba82c444104f8b43fb7e2e16ff7a7be3)]
|
|
32
|
+
|
|
33
|
+
#### :hammer_and_wrench: Others
|
|
34
|
+
|
|
35
|
+
- add edit button [ionlizarazu - [`d84f322`](https://github.com/eea/volto-clms-theme/commit/d84f3229c33dd41ae6741d46109cbafe25e01c54)]
|
|
36
|
+
- faq block [ionlizarazu - [`db09537`](https://github.com/eea/volto-clms-theme/commit/db095378f91b839cc8cdc40e395e8110aa6d19a7)]
|
|
37
|
+
- add dep [Mikel Larreategi - [`408b499`](https://github.com/eea/volto-clms-theme/commit/408b499a1caa7a1f1a48b18f37df7dc96aeec78b)]
|
|
38
|
+
- fix [Mikel Larreategi - [`840b896`](https://github.com/eea/volto-clms-theme/commit/840b89671d722e04c3b060bc527bdc68c25390cc)]
|
|
39
|
+
- remove console.log [Mikel Larreategi - [`bee5ceb`](https://github.com/eea/volto-clms-theme/commit/bee5ceb3273f0983c25a99782abff4490adbed5d)]
|
|
40
|
+
### [1.0.156](https://github.com/eea/volto-clms-theme/compare/1.0.155...1.0.156) - 12 December 2022
|
|
12
41
|
|
|
13
42
|
### [1.0.155](https://github.com/eea/volto-clms-theme/compare/1.0.154...1.0.155) - 12 December 2022
|
|
14
43
|
|
package/Jenkinsfile
CHANGED
|
@@ -41,19 +41,22 @@ pipeline {
|
|
|
41
41
|
|
|
42
42
|
"ES lint": {
|
|
43
43
|
node(label: 'docker') {
|
|
44
|
-
sh '''docker
|
|
44
|
+
sh '''docker pull plone/volto-addon-ci:15.x'''
|
|
45
|
+
sh '''docker run -i --rm --name="$BUILD_TAG-eslint" -e VOLTO=$VOLTO -e NAMESPACE="$NAMESPACE" -e GIT_NAME=$GIT_NAME -e GIT_BRANCH="$BRANCH_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" plone/volto-addon-ci:15.x eslint'''
|
|
45
46
|
}
|
|
46
47
|
},
|
|
47
48
|
|
|
48
49
|
"Style lint": {
|
|
49
50
|
node(label: 'docker') {
|
|
50
|
-
sh '''docker
|
|
51
|
+
sh '''docker pull plone/volto-addon-ci:15.x'''
|
|
52
|
+
sh '''docker run -i --rm --name="$BUILD_TAG-stylelint" -e VOLTO=$VOLTO -e NAMESPACE="$NAMESPACE" -e GIT_NAME=$GIT_NAME -e GIT_BRANCH="$BRANCH_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" plone/volto-addon-ci:15.x stylelint'''
|
|
51
53
|
}
|
|
52
54
|
},
|
|
53
55
|
|
|
54
56
|
"Prettier": {
|
|
55
57
|
node(label: 'docker') {
|
|
56
|
-
sh '''docker
|
|
58
|
+
sh '''docker pull plone/volto-addon-ci:15.x'''
|
|
59
|
+
sh '''docker run -i --rm --name="$BUILD_TAG-prettier" -e VOLTO=$VOLTO -e NAMESPACE="$NAMESPACE" -e GIT_NAME=$GIT_NAME -e GIT_BRANCH="$BRANCH_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" plone/volto-addon-ci:15.x prettier'''
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
)
|
|
@@ -77,8 +80,8 @@ pipeline {
|
|
|
77
80
|
node(label: 'docker') {
|
|
78
81
|
script {
|
|
79
82
|
try {
|
|
80
|
-
sh '''docker pull plone/volto-addon-ci'''
|
|
81
|
-
sh '''docker run -i --name="$BUILD_TAG-volto" -e NAMESPACE="$NAMESPACE" -e VOLTO=$VOLTO -e GIT_NAME=$GIT_NAME -e GIT_BRANCH="$BRANCH_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" plone/volto-addon-ci'''
|
|
83
|
+
sh '''docker pull plone/volto-addon-ci:15.x'''
|
|
84
|
+
sh '''docker run -i --name="$BUILD_TAG-volto" -e NAMESPACE="$NAMESPACE" -e VOLTO=$VOLTO -e GIT_NAME=$GIT_NAME -e GIT_BRANCH="$BRANCH_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" plone/volto-addon-ci:15.x'''
|
|
82
85
|
sh '''rm -rf xunit-reports'''
|
|
83
86
|
sh '''mkdir -p xunit-reports'''
|
|
84
87
|
sh '''docker cp $BUILD_TAG-volto:/opt/frontend/my-volto-project/coverage xunit-reports/'''
|
|
@@ -126,7 +129,7 @@ pipeline {
|
|
|
126
129
|
script {
|
|
127
130
|
try {
|
|
128
131
|
sh '''docker pull plone; docker run -d --rm --name="$BUILD_TAG-plone" -e SITE="Plone" -e PROFILES="profile-plone.restapi:blocks" plone fg'''
|
|
129
|
-
sh '''docker pull plone/volto-addon-ci; docker run -i --name="$BUILD_TAG-cypress" --link $BUILD_TAG-plone:plone -e VOLTO=$VOLTO -e NAMESPACE="$NAMESPACE" -e GIT_NAME=$GIT_NAME -e GIT_BRANCH="$BRANCH_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" -e DEPENDENCIES="$DEPENDENCIES" -e NODE_ENV=test plone/volto-addon-ci cypress'''
|
|
132
|
+
sh '''docker pull plone/volto-addon-ci:15.x; docker run -i --name="$BUILD_TAG-cypress" --link $BUILD_TAG-plone:plone -e VOLTO=$VOLTO -e NAMESPACE="$NAMESPACE" -e GIT_NAME=$GIT_NAME -e GIT_BRANCH="$BRANCH_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" -e DEPENDENCIES="$DEPENDENCIES" -e NODE_ENV=test plone/volto-addon-ci:15.x cypress'''
|
|
130
133
|
} finally {
|
|
131
134
|
try {
|
|
132
135
|
sh '''rm -rf cypress-reports cypress-results cypress-coverage'''
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eeacms/volto-clms-theme",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.158",
|
|
4
4
|
"description": "volto-clms-theme: Volto theme for CLMS site",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"author": "CodeSyntax for the European Environment Agency",
|
|
@@ -60,7 +60,8 @@
|
|
|
60
60
|
"volto-form-block": "2.8.0",
|
|
61
61
|
"react-input-range": "^1.3.0",
|
|
62
62
|
"lightgallery": "^2.4.0",
|
|
63
|
-
"validator": "13.7.0"
|
|
63
|
+
"validator": "13.7.0",
|
|
64
|
+
"connected-react-router": "6.8.0"
|
|
64
65
|
},
|
|
65
66
|
"devDependencies": {
|
|
66
67
|
"@cypress/code-coverage": "^3.9.5",
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CclTabs } from '@eeacms/volto-clms-theme/components/CclTab';
|
|
3
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
4
|
+
import { searchContent } from '@plone/volto/actions';
|
|
5
|
+
import config from '@plone/volto/registry';
|
|
6
|
+
import { Accordion, Segment } from 'semantic-ui-react';
|
|
7
|
+
import { Icon, UniversalLink } from '@plone/volto/components';
|
|
8
|
+
import AnimateHeight from 'react-animate-height';
|
|
9
|
+
import { StringToHTML } from '@eeacms/volto-clms-theme/components/CclUtils';
|
|
10
|
+
import penSVG from '@plone/volto/icons/pen.svg';
|
|
11
|
+
|
|
12
|
+
const CclFAQBlockView = (props) => {
|
|
13
|
+
const { isEditMode } = props;
|
|
14
|
+
const dispatch = useDispatch();
|
|
15
|
+
const path = useSelector((state) => state.router.location.pathname);
|
|
16
|
+
const search = useSelector((state) => state.search);
|
|
17
|
+
const handleClick = ({ index }) => {
|
|
18
|
+
const newIndex =
|
|
19
|
+
activeIndex.indexOf(index) === -1
|
|
20
|
+
? [...activeIndex, index]
|
|
21
|
+
: activeIndex.filter((item) => item !== index);
|
|
22
|
+
|
|
23
|
+
setActiveIndex(newIndex);
|
|
24
|
+
};
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
dispatch(
|
|
27
|
+
searchContent(path.replace('/edit', ''), {
|
|
28
|
+
fullobjects: 1,
|
|
29
|
+
portal_type: 'FAQ',
|
|
30
|
+
b_size: 999,
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
}, [path, dispatch]);
|
|
34
|
+
let categories =
|
|
35
|
+
search.items.length > 0
|
|
36
|
+
? [
|
|
37
|
+
...new Set(
|
|
38
|
+
search.items.map((item) => item.taxonomy_faqcategories).flat(),
|
|
39
|
+
),
|
|
40
|
+
]
|
|
41
|
+
.map((cat) => {
|
|
42
|
+
const cat_family = cat.title?.split(' » ');
|
|
43
|
+
if (cat_family.length > 1) {
|
|
44
|
+
cat['subTab'] = true;
|
|
45
|
+
}
|
|
46
|
+
return cat;
|
|
47
|
+
})
|
|
48
|
+
.filter(
|
|
49
|
+
(thing, index, self) =>
|
|
50
|
+
index === self.findIndex((t) => t.token === thing.token),
|
|
51
|
+
)
|
|
52
|
+
.sort((a, b) => {
|
|
53
|
+
if (a.title < b.title) {
|
|
54
|
+
return -1;
|
|
55
|
+
} else if (a.title > b.title) {
|
|
56
|
+
return 1;
|
|
57
|
+
}
|
|
58
|
+
return 0;
|
|
59
|
+
})
|
|
60
|
+
: [];
|
|
61
|
+
categories.forEach((cat, index) => {
|
|
62
|
+
if (cat.subTab && !categories[index - 1].subTab) {
|
|
63
|
+
categories[index - 1]['parent'] = true;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
const [activeIndex, setActiveIndex] = React.useState([0]);
|
|
67
|
+
const titleIcons = config.blocks?.blocksConfig?.accordion?.titleIcons;
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div id="faq-listing" className="ccl-container tab-container">
|
|
71
|
+
{search.loaded ? (
|
|
72
|
+
search.items?.length > 0 &&
|
|
73
|
+
categories.length > 0 && (
|
|
74
|
+
<CclTabs routing={true}>
|
|
75
|
+
{categories.map((cat, key) => {
|
|
76
|
+
const cat_family = cat.title.split(' » ');
|
|
77
|
+
const cat_title =
|
|
78
|
+
cat_family.length > 1
|
|
79
|
+
? cat_family[1].split('#')[1]
|
|
80
|
+
: cat_family[0].split('#')[1];
|
|
81
|
+
return (
|
|
82
|
+
<div
|
|
83
|
+
key={key}
|
|
84
|
+
tabTitle={cat_title}
|
|
85
|
+
className={cat_family.length > 1 ? 'subcard' : ''}
|
|
86
|
+
parent={cat.parent}
|
|
87
|
+
>
|
|
88
|
+
<div className="accordion-block">
|
|
89
|
+
{search.items.map((item, item_key) => {
|
|
90
|
+
return (
|
|
91
|
+
item.taxonomy_faqcategories.filter((faq_cat) =>
|
|
92
|
+
faq_cat.title.includes(cat.title),
|
|
93
|
+
).length > 0 && (
|
|
94
|
+
<Accordion fluid styled key={item_key}>
|
|
95
|
+
<Accordion.Title
|
|
96
|
+
as={'h2'}
|
|
97
|
+
onClick={() => handleClick({ index: item_key })}
|
|
98
|
+
className={'accordion-title align-arrow-right'}
|
|
99
|
+
>
|
|
100
|
+
{activeIndex.includes(item_key) ? (
|
|
101
|
+
<Icon name={titleIcons.opened.rightPosition} />
|
|
102
|
+
) : (
|
|
103
|
+
<Icon name={titleIcons.closed.rightPosition} />
|
|
104
|
+
)}
|
|
105
|
+
{isEditMode && (
|
|
106
|
+
<UniversalLink
|
|
107
|
+
openLinkInNewTab={true}
|
|
108
|
+
href={`${item['@id']}/edit`}
|
|
109
|
+
>
|
|
110
|
+
<Icon
|
|
111
|
+
name={penSVG}
|
|
112
|
+
className="circled"
|
|
113
|
+
title={'Edit'}
|
|
114
|
+
/>
|
|
115
|
+
</UniversalLink>
|
|
116
|
+
)}
|
|
117
|
+
<span>{item.title}</span>
|
|
118
|
+
</Accordion.Title>
|
|
119
|
+
<Accordion.Content
|
|
120
|
+
active={activeIndex.includes(item_key)}
|
|
121
|
+
>
|
|
122
|
+
<AnimateHeight
|
|
123
|
+
animateOpacity
|
|
124
|
+
duration={500}
|
|
125
|
+
height={'auto'}
|
|
126
|
+
>
|
|
127
|
+
<StringToHTML
|
|
128
|
+
string={item.text ? item.text.data : ''}
|
|
129
|
+
/>
|
|
130
|
+
</AnimateHeight>
|
|
131
|
+
</Accordion.Content>
|
|
132
|
+
</Accordion>
|
|
133
|
+
)
|
|
134
|
+
);
|
|
135
|
+
})}
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
);
|
|
139
|
+
})}
|
|
140
|
+
</CclTabs>
|
|
141
|
+
)
|
|
142
|
+
) : (
|
|
143
|
+
<Segment loading={search.loading}></Segment>
|
|
144
|
+
)}
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export default CclFAQBlockView;
|
|
@@ -58,6 +58,8 @@ import TextLinkCarouselEdit from '@eeacms/volto-clms-theme/components/Blocks/Ccl
|
|
|
58
58
|
import TextLinkCarouselView from '@eeacms/volto-clms-theme/components/Blocks/CclTextLinkCarouselBlock/CclTextLinkCarouselView';
|
|
59
59
|
import SubscriptionBlockView from '@eeacms/volto-clms-theme/components/Blocks/CclSubscriptionBlock/SubscriptionView';
|
|
60
60
|
import SubscriptionBlockEdit from '@eeacms/volto-clms-theme/components/Blocks/CclSubscriptionBlock/SubscriptionEdit';
|
|
61
|
+
import CclFAQBlockEdit from '@eeacms/volto-clms-theme/components/Blocks/CclFAQBlock/CclFAQBlockEdit';
|
|
62
|
+
import CclFAQBlockView from '@eeacms/volto-clms-theme/components/Blocks/CclFAQBlock/CclFAQBlockView';
|
|
61
63
|
import containerSVG from '@plone/volto/icons/apps.svg';
|
|
62
64
|
import {
|
|
63
65
|
customIdFieldSchema,
|
|
@@ -590,6 +592,22 @@ const customBlocks = (config) => ({
|
|
|
590
592
|
...config.blocks.blocksConfig.maps,
|
|
591
593
|
restricted: false,
|
|
592
594
|
},
|
|
595
|
+
cclFAQ: {
|
|
596
|
+
id: 'cclFAQ', // The name (id) of the block
|
|
597
|
+
title: 'FAQ Block', // The display name of the block
|
|
598
|
+
icon: containerSVG, // The icon used in the block chooser
|
|
599
|
+
group: 'ccl_blocks', // The group (blocks can be grouped, displayed in the chooser)
|
|
600
|
+
view: CclFAQBlockView, // The view mode component
|
|
601
|
+
edit: CclFAQBlockEdit, // The edit mode component
|
|
602
|
+
restricted: false, // If the block is restricted, it won't show in the chooser
|
|
603
|
+
mostUsed: false, // A meta group `most used`, appearing at the top of the chooser
|
|
604
|
+
blockHasOwnFocusManagement: false, // Set this to true if the block manages its own focus
|
|
605
|
+
sidebarTab: 1, // The sidebar tab you want to be selected when selecting the block
|
|
606
|
+
security: {
|
|
607
|
+
addPermission: [], // Future proof (not implemented yet) add user permission role(s)
|
|
608
|
+
view: [], // Future proof (not implemented yet) view user role(s)
|
|
609
|
+
},
|
|
610
|
+
},
|
|
593
611
|
});
|
|
594
612
|
|
|
595
613
|
export default customBlocks;
|
|
@@ -28,7 +28,9 @@ export const getPanels = (data) => {
|
|
|
28
28
|
|
|
29
29
|
export const slugify = (string) => {
|
|
30
30
|
return string
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
? string
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
.replace(/[\s-]+/g, '_')
|
|
34
|
+
.replace(/[^\w]+/g, '')
|
|
35
|
+
: '';
|
|
34
36
|
};
|
|
@@ -64,6 +64,12 @@ const CLMSDatasetDetailView = ({ content, token }) => {
|
|
|
64
64
|
const [open, setOpen] = React.useState({});
|
|
65
65
|
const locale = useSelector((state) => state.intl.locale);
|
|
66
66
|
|
|
67
|
+
const isAuxiliary = content.mapviewer_viewservice
|
|
68
|
+
.toLowerCase()
|
|
69
|
+
.startsWith(
|
|
70
|
+
'https://trial.discomap.eea.europa.eu/arcgis/services/clms/worldcountries/mapserver/wmsserver',
|
|
71
|
+
);
|
|
72
|
+
|
|
67
73
|
return (
|
|
68
74
|
<div className="ccl-container ">
|
|
69
75
|
<h1 className="page-title">{content.title}</h1>
|
|
@@ -422,7 +428,7 @@ const CLMSDatasetDetailView = ({ content, token }) => {
|
|
|
422
428
|
/>
|
|
423
429
|
</div>
|
|
424
430
|
)}
|
|
425
|
-
{content?.mapviewer_viewservice?.length > 0 && (
|
|
431
|
+
{content?.mapviewer_viewservice?.length > 0 && !isAuxiliary && (
|
|
426
432
|
<div className="menu-detail-button">
|
|
427
433
|
<CclButton
|
|
428
434
|
url={'/' + locale + '/map-viewer?dataset=' + content.UID}
|
|
@@ -461,12 +461,18 @@ const CLMSCartContent = (props) => {
|
|
|
461
461
|
}
|
|
462
462
|
<br />
|
|
463
463
|
<br />
|
|
464
|
-
<strong>Selected pre-packaged files:</strong>
|
|
464
|
+
<strong>Selected pre-packaged files from:</strong>
|
|
465
465
|
<ul>
|
|
466
|
-
{
|
|
467
|
-
|
|
466
|
+
{[
|
|
467
|
+
...new Set(
|
|
468
|
+
getSelectedCartItems()
|
|
469
|
+
.filter((item) => item.file_id)
|
|
470
|
+
.map((item) => item.name),
|
|
471
|
+
),
|
|
472
|
+
]
|
|
473
|
+
.sort()
|
|
468
474
|
.map((item, key) => (
|
|
469
|
-
<li key={key}>{item
|
|
475
|
+
<li key={key}>{item}</li>
|
|
470
476
|
))}
|
|
471
477
|
</ul>
|
|
472
478
|
<br />
|
|
@@ -17,6 +17,8 @@ function CclTab(props) {
|
|
|
17
17
|
routing,
|
|
18
18
|
redirect,
|
|
19
19
|
loginRequired,
|
|
20
|
+
className = '',
|
|
21
|
+
hasSubtab = false,
|
|
20
22
|
} = props;
|
|
21
23
|
const token = useSelector((state) => state.userSession?.token);
|
|
22
24
|
function onTabClick() {
|
|
@@ -25,22 +27,24 @@ function CclTab(props) {
|
|
|
25
27
|
const [redirecting, setRedirecting] = React.useState(false);
|
|
26
28
|
return (
|
|
27
29
|
<div
|
|
28
|
-
className={cx('card', activeTab === tabId ? 'active' : '')}
|
|
30
|
+
className={cx('card ' + className, activeTab === tabId ? 'active ' : '')}
|
|
29
31
|
onClick={(e) => {
|
|
30
32
|
!loginRequired
|
|
31
|
-
? onTabClick(e)
|
|
33
|
+
? !hasSubtab && onTabClick(e)
|
|
32
34
|
: loginRequired && token && onTabClick(e);
|
|
33
35
|
}}
|
|
34
36
|
onKeyDown={(e) => {
|
|
35
37
|
!loginRequired
|
|
36
|
-
? onTabClick(e)
|
|
38
|
+
? !hasSubtab && onTabClick(e)
|
|
37
39
|
: loginRequired && token && onTabClick(e);
|
|
38
40
|
}}
|
|
39
41
|
tabIndex="0"
|
|
40
42
|
role="button"
|
|
41
43
|
id={tabId}
|
|
42
44
|
>
|
|
43
|
-
{
|
|
45
|
+
{hasSubtab ? (
|
|
46
|
+
<span>{tabTitle}</span>
|
|
47
|
+
) : loginRequired && !token ? (
|
|
44
48
|
<CclLoginModal
|
|
45
49
|
otherPath={redirect ? redirect : undefined}
|
|
46
50
|
triggerComponent={() => (
|
|
@@ -4,6 +4,7 @@ import React, { useState } from 'react';
|
|
|
4
4
|
|
|
5
5
|
import CclTab from './CclTab';
|
|
6
6
|
import PropTypes from 'prop-types';
|
|
7
|
+
import { slugify } from '../Blocks/utils';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Tabs component documentation.
|
|
@@ -23,8 +24,7 @@ import PropTypes from 'prop-types';
|
|
|
23
24
|
const CclTabs = (props) => {
|
|
24
25
|
let { children, routing = false } = props;
|
|
25
26
|
let [activeTab, setActiveTab] = useState(
|
|
26
|
-
props.children[0].props.tabId ||
|
|
27
|
-
props.children[0].props.tabTitle.split(' ').join('-'),
|
|
27
|
+
props.children[0].props.tabId || slugify(props.children[0].props.tabTitle),
|
|
28
28
|
);
|
|
29
29
|
|
|
30
30
|
function onClickTabItem(tab) {
|
|
@@ -35,11 +35,11 @@ const CclTabs = (props) => {
|
|
|
35
35
|
const firstTab = children.filter((item) => !!item?.props?.tabTitle)[0];
|
|
36
36
|
if (routing) {
|
|
37
37
|
if (hash.startsWith('b_size')) {
|
|
38
|
-
setActiveTab(firstTab.props?.tabTitle
|
|
38
|
+
setActiveTab(slugify(firstTab.props?.tabTitle));
|
|
39
39
|
} else if (hash) {
|
|
40
40
|
setActiveTab(hash);
|
|
41
41
|
} else {
|
|
42
|
-
setActiveTab(firstTab.props?.tabTitle
|
|
42
|
+
setActiveTab(slugify(firstTab.props?.tabTitle));
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
}, [children, routing]);
|
|
@@ -53,7 +53,14 @@ const CclTabs = (props) => {
|
|
|
53
53
|
.filter((item) => !!item?.props?.tabTitle)
|
|
54
54
|
.map((child, key) => {
|
|
55
55
|
const { tabTitle, redirect } = child.props;
|
|
56
|
-
const tabId = tabTitle
|
|
56
|
+
const tabId = slugify(tabTitle);
|
|
57
|
+
let hasSubtab = false;
|
|
58
|
+
const currentTab = children.filter(
|
|
59
|
+
(item) => slugify(item?.props?.tabTitle) === tabId,
|
|
60
|
+
)[0];
|
|
61
|
+
if (currentTab?.props?.parent) {
|
|
62
|
+
hasSubtab = true;
|
|
63
|
+
}
|
|
57
64
|
return (
|
|
58
65
|
<CclTab
|
|
59
66
|
activeTab={activeTab}
|
|
@@ -63,6 +70,7 @@ const CclTabs = (props) => {
|
|
|
63
70
|
tabTitle={tabTitle}
|
|
64
71
|
onClick={onClickTabItem}
|
|
65
72
|
redirect={redirect}
|
|
73
|
+
hasSubtab={hasSubtab}
|
|
66
74
|
{...child.props}
|
|
67
75
|
/>
|
|
68
76
|
);
|
|
@@ -85,8 +93,7 @@ const CclTabs = (props) => {
|
|
|
85
93
|
.flat()
|
|
86
94
|
.filter((item) => !!item?.props?.tabTitle)
|
|
87
95
|
.map((child, index) => {
|
|
88
|
-
return child.props?.tabTitle
|
|
89
|
-
activeTab ? (
|
|
96
|
+
return slugify(child.props?.tabTitle) !== activeTab ? (
|
|
90
97
|
<div key={index} className="deactivate-content">
|
|
91
98
|
{child.props.children}
|
|
92
99
|
</div>
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff field component.
|
|
3
|
+
* @module components/manage/Diff/DiffField
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
// import { diffWords as dWords } from 'diff';
|
|
8
|
+
import { join, map } from 'lodash';
|
|
9
|
+
import PropTypes from 'prop-types';
|
|
10
|
+
import { Grid } from 'semantic-ui-react';
|
|
11
|
+
import ReactDOMServer from 'react-dom/server';
|
|
12
|
+
import { Provider } from 'react-intl-redux';
|
|
13
|
+
import { createBrowserHistory } from 'history';
|
|
14
|
+
// eslint-disable-next-line import/no-unresolved
|
|
15
|
+
import { ConnectedRouter } from 'connected-react-router';
|
|
16
|
+
import { useSelector } from 'react-redux';
|
|
17
|
+
|
|
18
|
+
import { Api } from '@plone/volto/helpers';
|
|
19
|
+
import configureStore from '@plone/volto/store';
|
|
20
|
+
import { DefaultView } from '@plone/volto/components/';
|
|
21
|
+
|
|
22
|
+
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Enhanced diff words utility
|
|
26
|
+
* @function diffWords
|
|
27
|
+
* @param oneStr Field one
|
|
28
|
+
* @param twoStr Field two
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Diff field component.
|
|
33
|
+
* @function DiffField
|
|
34
|
+
* @param {*} one Field one
|
|
35
|
+
* @param {*} two Field two
|
|
36
|
+
* @param {Object} schema Field schema
|
|
37
|
+
* @returns {string} Markup of the component.
|
|
38
|
+
*/
|
|
39
|
+
const DiffField = ({
|
|
40
|
+
one,
|
|
41
|
+
two,
|
|
42
|
+
contentOne,
|
|
43
|
+
contentTwo,
|
|
44
|
+
view,
|
|
45
|
+
schema,
|
|
46
|
+
diffLib,
|
|
47
|
+
}) => {
|
|
48
|
+
const language = useSelector((state) => state.intl.locale);
|
|
49
|
+
const readable_date_format = {
|
|
50
|
+
dateStyle: 'full',
|
|
51
|
+
timeStyle: 'short',
|
|
52
|
+
};
|
|
53
|
+
const diffWords = (oneStr, twoStr) => {
|
|
54
|
+
return diffLib.diffWords(String(oneStr), String(twoStr));
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
let parts, oneArray, twoArray;
|
|
58
|
+
if (schema.widget) {
|
|
59
|
+
switch (schema.widget) {
|
|
60
|
+
case 'richtext':
|
|
61
|
+
parts = diffWords(one?.data, two?.data);
|
|
62
|
+
break;
|
|
63
|
+
case 'datetime':
|
|
64
|
+
parts = diffWords(
|
|
65
|
+
new Intl.DateTimeFormat(language, readable_date_format).format(
|
|
66
|
+
new Date(one),
|
|
67
|
+
),
|
|
68
|
+
new Intl.DateTimeFormat(language, readable_date_format).format(
|
|
69
|
+
new Date(two),
|
|
70
|
+
),
|
|
71
|
+
);
|
|
72
|
+
break;
|
|
73
|
+
case 'json':
|
|
74
|
+
const api = new Api();
|
|
75
|
+
const history = createBrowserHistory();
|
|
76
|
+
const store = configureStore(window.__data, history, api);
|
|
77
|
+
parts = diffWords(
|
|
78
|
+
ReactDOMServer.renderToStaticMarkup(
|
|
79
|
+
<Provider store={store}>
|
|
80
|
+
<ConnectedRouter history={history}>
|
|
81
|
+
<DefaultView content={contentOne} />
|
|
82
|
+
</ConnectedRouter>
|
|
83
|
+
</Provider>,
|
|
84
|
+
),
|
|
85
|
+
ReactDOMServer.renderToStaticMarkup(
|
|
86
|
+
<Provider store={store}>
|
|
87
|
+
<ConnectedRouter history={history}>
|
|
88
|
+
<DefaultView content={contentTwo} />
|
|
89
|
+
</ConnectedRouter>
|
|
90
|
+
</Provider>,
|
|
91
|
+
),
|
|
92
|
+
);
|
|
93
|
+
break;
|
|
94
|
+
case 'textarea':
|
|
95
|
+
default:
|
|
96
|
+
parts = diffWords(one, two);
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
} else if (schema.type === 'object') {
|
|
100
|
+
parts = diffWords(one?.filename || one, two?.filename || two);
|
|
101
|
+
} else if (schema.type === 'array') {
|
|
102
|
+
oneArray = (one || []).map((i) => i?.title || i);
|
|
103
|
+
twoArray = (two || []).map((j) => j?.title || j);
|
|
104
|
+
parts = diffWords(oneArray, twoArray);
|
|
105
|
+
} else {
|
|
106
|
+
parts = diffWords(one?.title || one, two?.title || two);
|
|
107
|
+
}
|
|
108
|
+
return (
|
|
109
|
+
<Grid compact data-testid="DiffField">
|
|
110
|
+
<Grid.Row inverted>
|
|
111
|
+
<Grid.Column width={12}>{schema.title}</Grid.Column>
|
|
112
|
+
</Grid.Row>
|
|
113
|
+
{view === 'split' && (
|
|
114
|
+
<Grid.Row>
|
|
115
|
+
<Grid.Column width={6} verticalAlign="top">
|
|
116
|
+
<span
|
|
117
|
+
dangerouslySetInnerHTML={{
|
|
118
|
+
__html: join(
|
|
119
|
+
map(
|
|
120
|
+
parts,
|
|
121
|
+
(part) =>
|
|
122
|
+
(part.removed &&
|
|
123
|
+
`<span class="deletion">${part.value}</span>`) ||
|
|
124
|
+
(!part.added && `<span>${part.value}</span>`) ||
|
|
125
|
+
'',
|
|
126
|
+
),
|
|
127
|
+
'',
|
|
128
|
+
),
|
|
129
|
+
}}
|
|
130
|
+
/>
|
|
131
|
+
</Grid.Column>
|
|
132
|
+
<Grid.Column width={6} verticalAlign="top">
|
|
133
|
+
<span
|
|
134
|
+
dangerouslySetInnerHTML={{
|
|
135
|
+
__html: join(
|
|
136
|
+
map(
|
|
137
|
+
parts,
|
|
138
|
+
(part) =>
|
|
139
|
+
(part.added &&
|
|
140
|
+
`<span class="addition">${part.value}</span>`) ||
|
|
141
|
+
(!part.removed && `<span>${part.value}</span>`) ||
|
|
142
|
+
'',
|
|
143
|
+
),
|
|
144
|
+
'',
|
|
145
|
+
),
|
|
146
|
+
}}
|
|
147
|
+
/>
|
|
148
|
+
</Grid.Column>
|
|
149
|
+
</Grid.Row>
|
|
150
|
+
)}
|
|
151
|
+
{view === 'unified' && (
|
|
152
|
+
<Grid.Row>
|
|
153
|
+
<Grid.Column width={16} verticalAlign="top">
|
|
154
|
+
<span
|
|
155
|
+
dangerouslySetInnerHTML={{
|
|
156
|
+
__html: join(
|
|
157
|
+
map(
|
|
158
|
+
parts,
|
|
159
|
+
(part) =>
|
|
160
|
+
(part.removed &&
|
|
161
|
+
`<span class="deletion">${part.value}</span>`) ||
|
|
162
|
+
(part.added &&
|
|
163
|
+
`<span class="addition">${part.value}</span>`) ||
|
|
164
|
+
(!part.added && `<span>${part.value}</span>`),
|
|
165
|
+
),
|
|
166
|
+
'',
|
|
167
|
+
),
|
|
168
|
+
}}
|
|
169
|
+
/>
|
|
170
|
+
</Grid.Column>
|
|
171
|
+
</Grid.Row>
|
|
172
|
+
)}
|
|
173
|
+
</Grid>
|
|
174
|
+
);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Property types.
|
|
179
|
+
* @property {Object} propTypes Property types.
|
|
180
|
+
* @static
|
|
181
|
+
*/
|
|
182
|
+
DiffField.propTypes = {
|
|
183
|
+
one: PropTypes.any.isRequired,
|
|
184
|
+
two: PropTypes.any.isRequired,
|
|
185
|
+
contentOne: PropTypes.any,
|
|
186
|
+
contentTwo: PropTypes.any,
|
|
187
|
+
view: PropTypes.string.isRequired,
|
|
188
|
+
schema: PropTypes.shape({
|
|
189
|
+
widget: PropTypes.string,
|
|
190
|
+
type: PropTypes.string,
|
|
191
|
+
title: PropTypes.string,
|
|
192
|
+
}).isRequired,
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export default injectLazyLibs('diffLib')(DiffField);
|