@eeacms/volto-clms-theme 1.1.151 → 1.1.152

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 CHANGED
@@ -4,11 +4,16 @@ 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.1.151](https://github.com/eea/volto-clms-theme/compare/1.1.150...1.1.151) - 14 May 2024
7
+ ### [1.1.152](https://github.com/eea/volto-clms-theme/compare/1.1.151...1.1.152) - 22 May 2024
8
8
 
9
9
  #### :hammer_and_wrench: Others
10
10
 
11
- - add condition to the chouces[0].key [Ion Lizarazu - [`457a83d`](https://github.com/eea/volto-clms-theme/commit/457a83d26165d7b40c0b1b8e5fc6457ede05232a)]
11
+ - Projection naming popup [Ion Lizarazu - [`ab2414c`](https://github.com/eea/volto-clms-theme/commit/ab2414cb724dff204608edf850c6502b5254ac37)]
12
+ - add request param for selectors in volto-form-block [Ion Lizarazu - [`2b173aa`](https://github.com/eea/volto-clms-theme/commit/2b173aabef528a30e02dc35e553b7bf468166463)]
13
+ - fix TextareaWithRequestData request data [Ion Lizarazu - [`41ddf9e`](https://github.com/eea/volto-clms-theme/commit/41ddf9eb739e2bd560a206f58c8122f84766bdc0)]
14
+ - initial override for volto-form-block SelectWidget [Ion Lizarazu - [`47126ba`](https://github.com/eea/volto-clms-theme/commit/47126ba0d67424e8a7346713286f2d66678727e7)]
15
+ ### [1.1.151](https://github.com/eea/volto-clms-theme/compare/1.1.150...1.1.151) - 14 May 2024
16
+
12
17
  ### [1.1.150](https://github.com/eea/volto-clms-theme/compare/1.1.149...1.1.150) - 14 May 2024
13
18
 
14
19
  #### :hammer_and_wrench: Others
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-clms-theme",
3
- "version": "1.1.151",
3
+ "version": "1.1.152",
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",
@@ -66,6 +66,7 @@ import CclFAQBlockView from '@eeacms/volto-clms-theme/components/Blocks/CclFAQBl
66
66
  import CclHelpdeskDocBlockEdit from '@eeacms/volto-clms-theme/components/Blocks/CclHelpdeskDocBlock/CclHelpdeskDocBlockEdit';
67
67
  import CclHelpdeskDocBlockView from '@eeacms/volto-clms-theme/components/Blocks/CclHelpdeskDocBlock/CclHelpdeskDocBlockView';
68
68
  import containerSVG from '@plone/volto/icons/apps.svg';
69
+ import { SelectionSchemaExtender } from 'volto-form-block/components/FieldTypeSchemaExtenders';
69
70
  import {
70
71
  customIdFieldSchema,
71
72
  CheckboxSchemaExtender,
@@ -83,6 +84,7 @@ import infoSVG from '@plone/volto/icons/info.svg';
83
84
  import ImageWidget from '@eeacms/volto-clms-theme/components/Widgets/ImageWidget';
84
85
  import AttachmentWithSizeLimit from '@eeacms/volto-clms-theme/components/Widgets/AttachmentWithSizeLimit';
85
86
  import TextareaWithRequestData from '@eeacms/volto-clms-theme/components/Widgets/TextareaWithRequestData';
87
+ import SelectWithRequestWidget from '@eeacms/volto-clms-theme/components/Widgets/SelectWithRequestWidget';
86
88
  import TextWidget from '@plone/volto/components/manage/Widgets/TextWidget';
87
89
  import TextareaWidget from '@plone/volto/components/manage/Widgets/TextareaWidget';
88
90
  import EmailWidget from '@plone/volto/components/manage/Widgets/EmailWidget';
@@ -559,6 +561,7 @@ const customBlocks = (config) => ({
559
561
  fieldTypeSchemaExtenders: {
560
562
  ...config.blocks.blocksConfig.form.fieldTypeSchemaExtenders,
561
563
  checkbox_html: CheckboxSchemaExtender,
564
+ select_with_request_data: SelectionSchemaExtender,
562
565
  },
563
566
  additionalFields: [
564
567
  {
@@ -626,6 +629,11 @@ const customBlocks = (config) => ({
626
629
  label: 'CCL Text Area with Request Data',
627
630
  component: TextareaWithRequestData,
628
631
  },
632
+ {
633
+ id: 'select_with_request_data',
634
+ label: 'CCL List with Request Data',
635
+ component: SelectWithRequestWidget,
636
+ },
629
637
  ],
630
638
  },
631
639
  maps: {
@@ -1,8 +1,9 @@
1
1
  import React, { useEffect, useState } from 'react';
2
2
  import { useSelector } from 'react-redux';
3
- import { Select, Loader } from 'semantic-ui-react';
3
+ import { Select, Loader, Popup } from 'semantic-ui-react';
4
4
  import { baseSources } from '../../../constants/utmProjections';
5
5
  import { getUtm } from './utils';
6
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
6
7
 
7
8
  export const ProjectionNaming = ({ item, cartItems, setCartItems }) => {
8
9
  const [loading, setLoading] = useState(true);
@@ -93,18 +94,32 @@ export const ProjectionNaming = ({ item, cartItems, setCartItems }) => {
93
94
  loading ? (
94
95
  <Loader active inline />
95
96
  ) : (
96
- <Select
97
- placeholder="Select projection"
98
- value={
99
- item?.projection ??
100
- choices.find((ch) => ch.default)?.key ??
101
- choices[0]?.key
102
- }
103
- options={choices}
104
- onChange={(e, data) => {
105
- setProjectionValue(item?.unique_id, data.value, cartItems);
106
- }}
107
- />
97
+ <>
98
+ <Select
99
+ placeholder="Select projection"
100
+ value={
101
+ item?.projection ??
102
+ choices.find((ch) => ch.default)?.key ??
103
+ choices[0]?.key
104
+ }
105
+ options={choices}
106
+ onChange={(e, data) => {
107
+ setProjectionValue(item?.unique_id, data.value, cartItems);
108
+ }}
109
+ style={{ marginRight: '15%' }}
110
+ />
111
+ {!choices.find((ch) => ch.default) && choices.length === 4 && (
112
+ <Popup
113
+ content="You have selected an area that covers more that one EPSG code. For this we only offer to reproject the data to a EPSG code that includes the whole area selected by you"
114
+ trigger={
115
+ <FontAwesomeIcon
116
+ color="#000000"
117
+ icon={['fas', 'exclamation-circle']}
118
+ />
119
+ }
120
+ />
121
+ )}
122
+ </>
108
123
  )
109
124
  ) : (
110
125
  '-'
@@ -84,6 +84,12 @@ div.mb-2 {
84
84
  text-align: center;
85
85
  }
86
86
 
87
+ .ui.table td.table-td-projections {
88
+ display: flex;
89
+ align-items: center;
90
+ border-bottom: none;
91
+ }
92
+
87
93
  .cart-modal-trigger {
88
94
  border-color: #a0b128;
89
95
  color: #a0b128;
@@ -0,0 +1,32 @@
1
+ import React, { useEffect } from 'react';
2
+ import SelectWidget from 'volto-form-block/components/Widget/SelectWidget';
3
+ import { useLocation } from 'react-router-dom';
4
+
5
+ const SelectWithRequestWidget = (props) => {
6
+ const { onChange, id, choices, required, field_custom_id } = props;
7
+ const { search } = useLocation();
8
+ let params = new URLSearchParams(search);
9
+
10
+ useEffect(() => {
11
+ if (choices.find((ch) => ch[0] === params.get(field_custom_id))) {
12
+ onChange(
13
+ id,
14
+ choices.find((ch) => ch[0] === params.get(field_custom_id))[0],
15
+ );
16
+ }
17
+ // eslint-disable-next-line react-hooks/exhaustive-deps
18
+ }, [params.get(field_custom_id)]);
19
+ return (
20
+ <SelectWidget
21
+ {...props}
22
+ defaultValue={
23
+ choices.find((ch) => ch[0] === params.get(field_custom_id))
24
+ ? params.get(field_custom_id)
25
+ : null
26
+ }
27
+ noValueOption={!required}
28
+ />
29
+ );
30
+ };
31
+
32
+ export default SelectWithRequestWidget;
@@ -7,22 +7,21 @@ import TextareaWidget from '@plone/volto/components/manage/Widgets/TextareaWidge
7
7
  const TextareaWithRequestData = (props) => {
8
8
  const { onChange, id } = props;
9
9
  const { search } = useLocation();
10
-
11
- const hashMatch = decodeURIComponent(
12
- search.includes('text')
13
- ? search.match(/.*(\?text=.*)/)[1].replace(/\?text=/, '')
14
- : '',
15
- );
10
+ let params = new URLSearchParams(search);
16
11
 
17
12
  useEffect(() => {
18
- if (hashMatch) {
19
- onChange(id, hashMatch);
13
+ if (params.get('text')) {
14
+ onChange(id, params.get('text'));
20
15
  }
21
16
  // eslint-disable-next-line react-hooks/exhaustive-deps
22
- }, [hashMatch]);
17
+ }, [params.get('text')]);
23
18
 
24
19
  return (
25
- <TextareaWidget {...props} defaultValue={hashMatch} formHasErrors={false} />
20
+ <TextareaWidget
21
+ {...props}
22
+ defaultValue={params.get('text')}
23
+ formHasErrors={false}
24
+ />
26
25
  );
27
26
  };
28
27
 
@@ -235,6 +235,7 @@ const Field = ({
235
235
  isDisabled={disabled}
236
236
  formHasErrors={formHasErrors}
237
237
  invalid={isInvalid().toString()}
238
+ choices={[...(input_values?.map((v) => [v, v]) ?? [])]}
238
239
  {...(isInvalid() ? { className: 'is-invalid' } : {})}
239
240
  {...rest}
240
241
  />,
@@ -0,0 +1,326 @@
1
+ /**
2
+ * SelectWidget component.
3
+ * @module components/manage/Widgets/SelectWidget
4
+ * added aria- attributes
5
+ */
6
+
7
+ import React, { Component } from 'react';
8
+ import PropTypes from 'prop-types';
9
+ import { connect } from 'react-redux';
10
+ import { compose } from 'redux';
11
+ import { map } from 'lodash';
12
+ import { defineMessages, injectIntl } from 'react-intl';
13
+ import {
14
+ getVocabFromHint,
15
+ getVocabFromField,
16
+ getVocabFromItems,
17
+ } from '@plone/volto/helpers';
18
+ import { FormFieldWrapper } from '@plone/volto/components';
19
+ import { getVocabulary, getVocabularyTokenTitle } from '@plone/volto/actions';
20
+ import { normalizeValue } from '@plone/volto/components/manage/Widgets/SelectUtils';
21
+
22
+ import {
23
+ customSelectStyles,
24
+ DropdownIndicator,
25
+ ClearIndicator,
26
+ Option,
27
+ selectTheme,
28
+ MenuList,
29
+ } from '@plone/volto/components/manage/Widgets/SelectStyling';
30
+ import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
31
+ import {
32
+ getReactSelectAriaLiveMessages,
33
+ getReactSelectScreenReaderStatus,
34
+ } from 'volto-form-block/helpers/react-select';
35
+
36
+ const messages = defineMessages({
37
+ default: {
38
+ id: 'Default',
39
+ defaultMessage: 'Default',
40
+ },
41
+ idTitle: {
42
+ id: 'Short Name',
43
+ defaultMessage: 'Short Name',
44
+ },
45
+ idDescription: {
46
+ id: 'Used for programmatic access to the fieldset.',
47
+ defaultMessage: 'Used for programmatic access to the fieldset.',
48
+ },
49
+ title: {
50
+ id: 'Title',
51
+ defaultMessage: 'Title',
52
+ },
53
+ description: {
54
+ id: 'Description',
55
+ defaultMessage: 'Description',
56
+ },
57
+ close: {
58
+ id: 'Close',
59
+ defaultMessage: 'Close',
60
+ },
61
+ choices: {
62
+ id: 'Choices',
63
+ defaultMessage: 'Choices',
64
+ },
65
+ required: {
66
+ id: 'Required',
67
+ defaultMessage: 'Required',
68
+ },
69
+ select: {
70
+ id: 'Select…',
71
+ defaultMessage: 'Select…',
72
+ },
73
+ no_value: {
74
+ id: 'No value',
75
+ defaultMessage: 'No value',
76
+ },
77
+ no_options: {
78
+ id: 'No options',
79
+ defaultMessage: 'No options',
80
+ },
81
+ });
82
+
83
+ /**
84
+ * SelectWidget component class.
85
+ * @function SelectWidget
86
+ * @returns {string} Markup of the component.
87
+ */
88
+ class SelectWidget extends Component {
89
+ /**
90
+ * Property types.
91
+ * @property {Object} propTypes Property types.
92
+ * @static
93
+ */
94
+ static propTypes = {
95
+ id: PropTypes.string.isRequired,
96
+ title: PropTypes.string.isRequired,
97
+ description: PropTypes.string,
98
+ required: PropTypes.bool,
99
+ error: PropTypes.arrayOf(PropTypes.string),
100
+ getVocabulary: PropTypes.func.isRequired,
101
+ getVocabularyTokenTitle: PropTypes.func.isRequired,
102
+ choices: PropTypes.arrayOf(
103
+ PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
104
+ ),
105
+ items: PropTypes.shape({
106
+ vocabulary: PropTypes.object,
107
+ }),
108
+ widgetOptions: PropTypes.shape({
109
+ vocabulary: PropTypes.object,
110
+ }),
111
+ value: PropTypes.oneOfType([
112
+ PropTypes.object,
113
+ PropTypes.string,
114
+ PropTypes.bool,
115
+ PropTypes.func,
116
+ PropTypes.array,
117
+ ]),
118
+ onChange: PropTypes.func.isRequired,
119
+ onBlur: PropTypes.func,
120
+ onClick: PropTypes.func,
121
+ onEdit: PropTypes.func,
122
+ onDelete: PropTypes.func,
123
+ wrapped: PropTypes.bool,
124
+ noValueOption: PropTypes.bool,
125
+ customOptionStyling: PropTypes.any,
126
+ isMulti: PropTypes.bool,
127
+ placeholder: PropTypes.string,
128
+ };
129
+
130
+ /**
131
+ * Default properties
132
+ * @property {Object} defaultProps Default properties.
133
+ * @static
134
+ */
135
+ static defaultProps = {
136
+ description: null,
137
+ required: false,
138
+ items: {
139
+ vocabulary: null,
140
+ },
141
+ widgetOptions: {
142
+ vocabulary: null,
143
+ },
144
+ error: [],
145
+ choices: [],
146
+ value: null,
147
+ onChange: () => {},
148
+ onBlur: () => {},
149
+ onClick: () => {},
150
+ onEdit: null,
151
+ onDelete: null,
152
+ noValueOption: true,
153
+ customOptionStyling: null,
154
+ };
155
+
156
+ /**
157
+ * Component did mount
158
+ * @method componentDidMount
159
+ * @returns {undefined}
160
+ */
161
+ componentDidMount() {
162
+ if (
163
+ (!this.props.choices || this.props.choices?.length === 0) &&
164
+ this.props.vocabBaseUrl
165
+ ) {
166
+ this.props.getVocabulary({
167
+ vocabNameOrURL: this.props.vocabBaseUrl,
168
+ size: -1,
169
+ subrequest: this.props.intl.locale,
170
+ });
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Render method.
176
+ * @method render
177
+ * @returns {string} Markup for the component.
178
+ */
179
+ render() {
180
+ const {
181
+ id,
182
+ choices,
183
+ value,
184
+ defaultValue,
185
+ intl,
186
+ onChange,
187
+ required,
188
+ invalid,
189
+ title,
190
+ } = this.props;
191
+ const normalizedValue = normalizeValue(
192
+ choices,
193
+ value ?? defaultValue,
194
+ intl,
195
+ );
196
+ // Make sure that both disabled and isDisabled (from the DX layout feat work)
197
+ const disabled = this.props.disabled || this.props.isDisabled;
198
+ const Select = this.props.reactSelect.default;
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 && !this.props.default
212
+ ? [
213
+ {
214
+ label: this.props.intl.formatMessage(messages.no_value),
215
+ value: 'no-value',
216
+ },
217
+ ]
218
+ : []),
219
+ ];
220
+
221
+ const isMulti = this.props.isMulti
222
+ ? this.props.isMulti
223
+ : id === 'roles' || id === 'groups';
224
+
225
+ let attributes = {};
226
+ if (required) {
227
+ attributes.required = true;
228
+ attributes['aria-required'] = true;
229
+ }
230
+
231
+ const isInvalid = invalid === true || invalid === 'true';
232
+ if (isInvalid) {
233
+ attributes['aria-invalid'] = true;
234
+ }
235
+
236
+ return (
237
+ <FormFieldWrapper {...this.props}>
238
+ <Select
239
+ id={`field-${id}`}
240
+ key={choices}
241
+ name={id}
242
+ isDisabled={disabled}
243
+ isSearchable={true}
244
+ className="react-select-container"
245
+ classNamePrefix="react-select"
246
+ isMulti={isMulti}
247
+ options={options}
248
+ defaultValue={defaultValue}
249
+ {...attributes}
250
+ styles={customSelectStyles}
251
+ theme={selectTheme}
252
+ components={{
253
+ ...(options?.length > 25 && {
254
+ MenuList,
255
+ }),
256
+ DropdownIndicator,
257
+ ClearIndicator,
258
+ Option: this.props.customOptionStyling || Option,
259
+ }}
260
+ value={normalizedValue}
261
+ placeholder={
262
+ this.props.placeholder ??
263
+ this.props.intl.formatMessage(messages.select)
264
+ }
265
+ onChange={(selectedOption) => {
266
+ if (isMulti) {
267
+ return onChange(
268
+ id,
269
+ selectedOption.map((el) => el.value),
270
+ );
271
+ }
272
+ return onChange(
273
+ id,
274
+ selectedOption && selectedOption.value !== 'no-value'
275
+ ? selectedOption.value
276
+ : undefined,
277
+ );
278
+ }}
279
+ isClearable
280
+ aria-label={title}
281
+ ariaLiveMessages={getReactSelectAriaLiveMessages(intl)}
282
+ screenReaderStatus={getReactSelectScreenReaderStatus(intl)}
283
+ />
284
+ </FormFieldWrapper>
285
+ );
286
+ }
287
+ }
288
+
289
+ export const SelectWidgetComponent = injectIntl(SelectWidget);
290
+
291
+ export default compose(
292
+ injectLazyLibs(['reactSelect']),
293
+ connect(
294
+ (state, props) => {
295
+ const vocabBaseUrl = !props.choices
296
+ ? getVocabFromHint(props) ||
297
+ getVocabFromField(props) ||
298
+ getVocabFromItems(props)
299
+ : '';
300
+
301
+ const vocabState =
302
+ state.vocabularies?.[vocabBaseUrl]?.subrequests?.[props.intl.locale];
303
+
304
+ // If the schema already has the choices in it, then do not try to get the vocab,
305
+ // even if there is one
306
+ if (props.choices) {
307
+ return {
308
+ choices: props.choices,
309
+ };
310
+ } else if (vocabState) {
311
+ return {
312
+ vocabBaseUrl,
313
+ choices: vocabState?.items ?? [],
314
+ };
315
+ // There is a moment that vocabState is not there yet, so we need to pass the
316
+ // vocabBaseUrl to the component.
317
+ } else if (vocabBaseUrl) {
318
+ return {
319
+ vocabBaseUrl,
320
+ };
321
+ }
322
+ return {};
323
+ },
324
+ { getVocabulary, getVocabularyTokenTitle },
325
+ ),
326
+ )(SelectWidgetComponent);