@eeacms/volto-clms-theme 1.0.132 → 1.0.133
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 +14 -0
- package/package.json +1 -1
- package/src/components/Blocks/CustomTemplates/VoltoFormBlock/View.jsx +274 -0
- package/src/components/Blocks/CustomTemplates/VoltoTabsBlock/CclVerticalFaqTabsView.jsx +2 -2
- package/src/components/Blocks/CustomTemplates/VoltoTabsBlock/CclVerticalTabsView.jsx +2 -2
- package/src/components/Blocks/CustomTemplates/VoltoTabsBlock/RoutingHOC.jsx +4 -4
- package/src/components/Blocks/customBlocks.js +2 -1
- package/theme/clms/css/styles.less +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@ 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.133](https://github.com/eea/volto-clms-theme/compare/1.0.132...1.0.133) - 6 October 2022
|
|
8
|
+
|
|
9
|
+
#### :bug: Bug Fixes
|
|
10
|
+
|
|
11
|
+
- fix: remove padding from datset download table segment [joewdavies - [`323857c`](https://github.com/eea/volto-clms-theme/commit/323857c3c4f02e31e6bae880b9c4feda7807932e)]
|
|
12
|
+
- fix: hash for FAQ tabs [ionlizarazu - [`10621d1`](https://github.com/eea/volto-clms-theme/commit/10621d15f2a83405d60109874e0a03798873de28)]
|
|
13
|
+
- fix: tabs hash with search inside CLMS-1438 [ionlizarazu - [`1cea1b0`](https://github.com/eea/volto-clms-theme/commit/1cea1b0b8dc772e6ca2beb8831f6625f8a58166e)]
|
|
14
|
+
- fix: fields order for the email in volto-form-block [ionlizarazu - [`798dcb3`](https://github.com/eea/volto-clms-theme/commit/798dcb38defe79924ba5b1aa4eace10d99235bcb)]
|
|
15
|
+
- fix: manage the image_field_widget as attachment [ionlizarazu - [`c93869f`](https://github.com/eea/volto-clms-theme/commit/c93869f9d3e05f7dba4be7398d73b7dae9b276b5)]
|
|
16
|
+
- fix: CLMS-1424 remove word-break from cart table cells to prevent compaction [joewdavies - [`8b43cb0`](https://github.com/eea/volto-clms-theme/commit/8b43cb0486597c82d25c5e547801da4645706bdd)]
|
|
17
|
+
|
|
18
|
+
#### :hammer_and_wrench: Others
|
|
19
|
+
|
|
20
|
+
- override and use our volto-form-block View.jsx [ionlizarazu - [`f47511a`](https://github.com/eea/volto-clms-theme/commit/f47511a5c50b82d411e9b3c4fe45287f728bca17)]
|
|
7
21
|
### [1.0.132](https://github.com/eea/volto-clms-theme/compare/1.0.131...1.0.132) - 3 October 2022
|
|
8
22
|
|
|
9
23
|
#### :rocket: New Features
|
package/package.json
CHANGED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import React, { useState, useEffect, useReducer, useRef } from 'react';
|
|
2
|
+
import { useSelector, useDispatch } from 'react-redux';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import { useIntl, defineMessages } from 'react-intl';
|
|
5
|
+
import { submitForm } from 'volto-form-block/actions';
|
|
6
|
+
import { getFieldName } from 'volto-form-block/components/utils';
|
|
7
|
+
import FormView from 'volto-form-block/components/FormView';
|
|
8
|
+
import { formatDate } from '@plone/volto/helpers/Utils/Date';
|
|
9
|
+
import config from '@plone/volto/registry';
|
|
10
|
+
import Captcha from 'volto-form-block/components/Widget/Captcha';
|
|
11
|
+
|
|
12
|
+
const messages = defineMessages({
|
|
13
|
+
formSubmitted: {
|
|
14
|
+
id: 'formSubmitted',
|
|
15
|
+
defaultMessage: 'Form successfully submitted',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const initialState = {
|
|
20
|
+
loading: false,
|
|
21
|
+
error: null,
|
|
22
|
+
result: null,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const FORM_STATES = {
|
|
26
|
+
normal: 'normal',
|
|
27
|
+
loading: 'loading',
|
|
28
|
+
error: 'error',
|
|
29
|
+
success: 'success',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const formStateReducer = (state, action) => {
|
|
33
|
+
switch (action.type) {
|
|
34
|
+
case FORM_STATES.normal:
|
|
35
|
+
return initialState;
|
|
36
|
+
|
|
37
|
+
case FORM_STATES.loading:
|
|
38
|
+
return { loading: true, error: null, result: null };
|
|
39
|
+
|
|
40
|
+
case FORM_STATES.error:
|
|
41
|
+
return { loading: false, error: action.error, result: null };
|
|
42
|
+
|
|
43
|
+
case FORM_STATES.success:
|
|
44
|
+
return { loading: false, error: null, result: action.result };
|
|
45
|
+
|
|
46
|
+
default:
|
|
47
|
+
return initialState;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const getInitialData = (data) => ({
|
|
52
|
+
...data.reduce(
|
|
53
|
+
(acc, field) => ({ ...acc, [getFieldName(field.label, field.id)]: field }),
|
|
54
|
+
{},
|
|
55
|
+
),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Form view
|
|
60
|
+
* @class View
|
|
61
|
+
*/
|
|
62
|
+
const View = ({ data, id, path }) => {
|
|
63
|
+
const intl = useIntl();
|
|
64
|
+
const dispatch = useDispatch();
|
|
65
|
+
const { static_fields = [] } = data;
|
|
66
|
+
|
|
67
|
+
const [formData, setFormData] = useReducer((state, action) => {
|
|
68
|
+
if (action.reset) {
|
|
69
|
+
return getInitialData(static_fields);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
...state,
|
|
74
|
+
[action.field]: action.value,
|
|
75
|
+
};
|
|
76
|
+
}, getInitialData(static_fields));
|
|
77
|
+
|
|
78
|
+
const [formState, setFormState] = useReducer(formStateReducer, initialState);
|
|
79
|
+
const [formErrors, setFormErrors] = useState([]);
|
|
80
|
+
const submitResults = useSelector((state) => state.submitForm);
|
|
81
|
+
const captchaToken = useRef();
|
|
82
|
+
|
|
83
|
+
const onChangeFormData = (field_id, field, value, extras) => {
|
|
84
|
+
setFormData({ field, value: { field_id, value, ...extras } });
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (formErrors.length > 0) {
|
|
89
|
+
isValidForm();
|
|
90
|
+
}
|
|
91
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
92
|
+
}, [formData]);
|
|
93
|
+
|
|
94
|
+
const isValidForm = () => {
|
|
95
|
+
const v = [];
|
|
96
|
+
data.subblocks.forEach((subblock, index) => {
|
|
97
|
+
const name = getFieldName(subblock.label, subblock.id);
|
|
98
|
+
const fieldType = subblock.field_type;
|
|
99
|
+
const additionalField =
|
|
100
|
+
config.blocks.blocksConfig.form.additionalFields?.filter(
|
|
101
|
+
(f) => f.id === fieldType && f.isValid !== undefined,
|
|
102
|
+
)?.[0] ?? null;
|
|
103
|
+
if (
|
|
104
|
+
subblock.required &&
|
|
105
|
+
additionalField &&
|
|
106
|
+
!additionalField?.isValid(formData, name)
|
|
107
|
+
) {
|
|
108
|
+
v.push(name);
|
|
109
|
+
} else if (
|
|
110
|
+
subblock.required &&
|
|
111
|
+
fieldType === 'checkbox' &&
|
|
112
|
+
!formData[name]?.value
|
|
113
|
+
) {
|
|
114
|
+
v.push(name);
|
|
115
|
+
} else if (
|
|
116
|
+
subblock.required &&
|
|
117
|
+
(!formData[name] ||
|
|
118
|
+
formData[name]?.value?.length === 0 ||
|
|
119
|
+
JSON.stringify(formData[name]?.value ?? {}) === '{}')
|
|
120
|
+
) {
|
|
121
|
+
v.push(name);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (data.captcha && !captchaToken.current) {
|
|
126
|
+
v.push('captcha');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
setFormErrors(v);
|
|
130
|
+
return v.length === 0;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const submit = (e) => {
|
|
134
|
+
e.preventDefault();
|
|
135
|
+
captcha
|
|
136
|
+
.verify()
|
|
137
|
+
.then(() => {
|
|
138
|
+
if (isValidForm()) {
|
|
139
|
+
let attachments = {};
|
|
140
|
+
let captcha = {
|
|
141
|
+
provider: data.captcha,
|
|
142
|
+
token: captchaToken.current,
|
|
143
|
+
};
|
|
144
|
+
if (data.captcha === 'honeypot') {
|
|
145
|
+
captcha.value = formData[data.captcha_props.id]?.value ?? '';
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let formattedFormData = { ...formData };
|
|
149
|
+
data.subblocks.forEach((subblock) => {
|
|
150
|
+
let name = getFieldName(subblock.label, subblock.id);
|
|
151
|
+
if (formattedFormData[name]?.value) {
|
|
152
|
+
formattedFormData[name].field_id = subblock.field_id;
|
|
153
|
+
// const isAttachment = subblock.field_type === 'attachment';
|
|
154
|
+
const isAttachment = [
|
|
155
|
+
'attachment',
|
|
156
|
+
'image_field_widget',
|
|
157
|
+
].includes(subblock.field_type);
|
|
158
|
+
const isDate = subblock.field_type === 'date';
|
|
159
|
+
|
|
160
|
+
if (isAttachment) {
|
|
161
|
+
attachments[name] = formattedFormData[name].value;
|
|
162
|
+
delete formattedFormData[name];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (isDate) {
|
|
166
|
+
formattedFormData[name].value = formatDate({
|
|
167
|
+
date: formattedFormData[name].value,
|
|
168
|
+
format: 'DD-MM-YYYY',
|
|
169
|
+
locale: intl.locale,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const sortedFormattedFormData = Object.keys(formattedFormData)
|
|
176
|
+
.sort((a, b) => {
|
|
177
|
+
return (
|
|
178
|
+
data.subblocks
|
|
179
|
+
.map((subblock) => getFieldName(subblock.label, subblock.id))
|
|
180
|
+
.indexOf(a) -
|
|
181
|
+
data.subblocks
|
|
182
|
+
.map((subblock) => getFieldName(subblock.label, subblock.id))
|
|
183
|
+
.indexOf(b)
|
|
184
|
+
);
|
|
185
|
+
})
|
|
186
|
+
.reduce((accumulator, key) => {
|
|
187
|
+
accumulator[key] = formattedFormData[key];
|
|
188
|
+
|
|
189
|
+
return accumulator;
|
|
190
|
+
}, {});
|
|
191
|
+
dispatch(
|
|
192
|
+
submitForm(
|
|
193
|
+
path,
|
|
194
|
+
id,
|
|
195
|
+
Object.keys(sortedFormattedFormData).map((name) => ({
|
|
196
|
+
...sortedFormattedFormData[name],
|
|
197
|
+
})),
|
|
198
|
+
attachments,
|
|
199
|
+
captcha,
|
|
200
|
+
),
|
|
201
|
+
);
|
|
202
|
+
setFormState({ type: FORM_STATES.loading });
|
|
203
|
+
} else {
|
|
204
|
+
setFormState({ type: FORM_STATES.error });
|
|
205
|
+
}
|
|
206
|
+
})
|
|
207
|
+
.catch(() => {
|
|
208
|
+
setFormState({ type: FORM_STATES.error });
|
|
209
|
+
});
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const resetFormState = () => {
|
|
213
|
+
setFormData({ reset: true });
|
|
214
|
+
setFormState({ type: FORM_STATES.normal });
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const resetFormOnError = () => {
|
|
218
|
+
setFormState({ type: FORM_STATES.normal });
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const captcha = new Captcha({
|
|
222
|
+
captchaToken,
|
|
223
|
+
captcha: data.captcha,
|
|
224
|
+
captcha_props: data.captcha_props,
|
|
225
|
+
onChangeFormData,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
if (submitResults?.loaded) {
|
|
230
|
+
setFormState({
|
|
231
|
+
type: FORM_STATES.success,
|
|
232
|
+
result: intl.formatMessage(messages.formSubmitted),
|
|
233
|
+
});
|
|
234
|
+
captcha.reset();
|
|
235
|
+
} else if (submitResults?.error) {
|
|
236
|
+
let errorDescription = `${
|
|
237
|
+
JSON.parse(submitResults.error.response?.text ?? '{}')?.message
|
|
238
|
+
}`;
|
|
239
|
+
|
|
240
|
+
setFormState({ type: FORM_STATES.error, error: errorDescription });
|
|
241
|
+
}
|
|
242
|
+
captchaToken.current = undefined;
|
|
243
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
244
|
+
}, [submitResults]);
|
|
245
|
+
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
resetFormState();
|
|
248
|
+
}, []);
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<FormView
|
|
252
|
+
formState={formState}
|
|
253
|
+
formErrors={formErrors}
|
|
254
|
+
formData={formData}
|
|
255
|
+
captcha={captcha}
|
|
256
|
+
onChangeFormData={onChangeFormData}
|
|
257
|
+
data={data}
|
|
258
|
+
onSubmit={submit}
|
|
259
|
+
resetFormState={resetFormState}
|
|
260
|
+
resetFormOnError={resetFormOnError}
|
|
261
|
+
/>
|
|
262
|
+
);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Property types.
|
|
267
|
+
* @property {Object} propTypes Property types.
|
|
268
|
+
* @static
|
|
269
|
+
*/
|
|
270
|
+
View.propTypes = {
|
|
271
|
+
data: PropTypes.objectOf(PropTypes.any).isRequired,
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
export default View;
|
|
@@ -25,7 +25,7 @@ const CclVerticalFaqTabsView = (props) => {
|
|
|
25
25
|
<div className="right-content cont-w-75">
|
|
26
26
|
{tabsList.map((tab, index) => {
|
|
27
27
|
const title = tabs[tab].title;
|
|
28
|
-
const tabHash = slugify(title)
|
|
28
|
+
const tabHash = `tab=${slugify(title)}`;
|
|
29
29
|
return (
|
|
30
30
|
<Route to={'#' + tabHash}>
|
|
31
31
|
<div
|
|
@@ -64,7 +64,7 @@ const CclVerticalFaqTabsView = (props) => {
|
|
|
64
64
|
const nextSubTab =
|
|
65
65
|
tabs[tabsList[tabIndex]]?.subTab?.subtab || false;
|
|
66
66
|
const defaultTitle = `Tab ${tabIndex}`;
|
|
67
|
-
const tabHash = title
|
|
67
|
+
const tabHash = `tab=${slugify(title)}`;
|
|
68
68
|
return (
|
|
69
69
|
<div
|
|
70
70
|
key={index}
|
|
@@ -37,7 +37,7 @@ const CclVerticalTabsView = (props) => {
|
|
|
37
37
|
<div className="right-content cont-w-75">
|
|
38
38
|
{tabsList.map((tab, index) => {
|
|
39
39
|
const title = tabs[tab].title;
|
|
40
|
-
const tabHash = slugify(title)
|
|
40
|
+
const tabHash = `tab=${slugify(title)}`;
|
|
41
41
|
return (
|
|
42
42
|
<Route key={index} to={'#' + tabHash}>
|
|
43
43
|
<div
|
|
@@ -70,7 +70,7 @@ const CclVerticalTabsView = (props) => {
|
|
|
70
70
|
const nextSubTab =
|
|
71
71
|
tabs[tabsList[tabIndex]]?.subTab?.subtab || false;
|
|
72
72
|
const defaultTitle = `Tab ${tabIndex}`;
|
|
73
|
-
const tabHash = slugify(title)
|
|
73
|
+
const tabHash = `tab=${slugify(title)}`;
|
|
74
74
|
return (
|
|
75
75
|
<div
|
|
76
76
|
key={index}
|
|
@@ -29,12 +29,12 @@ const RoutingHOC = (TabView) =>
|
|
|
29
29
|
// return rTabsList[window.location.hash.match(/.*&?#?tab=(.*)/)[1] - 1];
|
|
30
30
|
// }
|
|
31
31
|
if (
|
|
32
|
-
window.location.hash.match(
|
|
33
|
-
window.location.hash.match(
|
|
32
|
+
window.location.hash.match(/.*([&|#]tab=.*)/) &&
|
|
33
|
+
window.location.hash.match(/.*([&|#]tab=.*)/).length > 1
|
|
34
34
|
) {
|
|
35
35
|
const hashMatch = window.location.hash
|
|
36
|
-
.match(
|
|
37
|
-
.replace(
|
|
36
|
+
.match(/.*([&|#]tab=.*)/)[1]
|
|
37
|
+
.replace(/[&|#]tab=/, '');
|
|
38
38
|
const result = tabsDict.filter((t) => slugify(t.title) === hashMatch);
|
|
39
39
|
if (result.length > 0) {
|
|
40
40
|
return result[0].id;
|
|
@@ -74,7 +74,7 @@ import ImageWidget from '@eeacms/volto-clms-theme/components/Widgets/ImageWidget
|
|
|
74
74
|
import TextWidget from '@plone/volto/components/manage/Widgets/TextWidget';
|
|
75
75
|
import TextareaWidget from '@plone/volto/components/manage/Widgets/TextareaWidget';
|
|
76
76
|
import EmailWidget from '@plone/volto/components/manage/Widgets/EmailWidget';
|
|
77
|
-
|
|
77
|
+
import { default as FormCustomView } from '@eeacms/volto-clms-theme/components/Blocks/CustomTemplates/VoltoFormBlock/View';
|
|
78
78
|
export const customGroupBlocksOrder = [
|
|
79
79
|
{
|
|
80
80
|
id: 'ccl_blocks',
|
|
@@ -481,6 +481,7 @@ const customBlocks = (config) => ({
|
|
|
481
481
|
},
|
|
482
482
|
form: {
|
|
483
483
|
...config.blocks.blocksConfig.form,
|
|
484
|
+
view: FormCustomView,
|
|
484
485
|
fieldSchema: customIdFieldSchema,
|
|
485
486
|
fieldTypeSchemaExtenders: {
|
|
486
487
|
checkbox_html: CheckboxSchemaExtender,
|
|
@@ -940,6 +940,11 @@ body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
|
|
|
940
940
|
vertical-align: middle;
|
|
941
941
|
}
|
|
942
942
|
|
|
943
|
+
.dataset-download-table > .ui.basic.segment {
|
|
944
|
+
padding-left: 0px;
|
|
945
|
+
padding-right: 0px;
|
|
946
|
+
}
|
|
947
|
+
|
|
943
948
|
/* Downloads */
|
|
944
949
|
.download-block {
|
|
945
950
|
position: relative;
|
|
@@ -1251,4 +1256,9 @@ div#page-document h1.documentFirstHeading {
|
|
|
1251
1256
|
#sidebar-metadata .react-select__option {
|
|
1252
1257
|
white-space: nowrap;
|
|
1253
1258
|
width: fit-content;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
// CLMS-1424
|
|
1262
|
+
.cart-table td {
|
|
1263
|
+
word-break: unset !important;
|
|
1254
1264
|
}
|