@eeacms/volto-tableau 7.0.5 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -17
- package/jest.setup.js +29 -2
- package/locales/de/LC_MESSAGES/volto.po +55 -0
- package/locales/en/LC_MESSAGES/volto.po +55 -0
- package/locales/it/LC_MESSAGES/volto.po +55 -0
- package/locales/ro/LC_MESSAGES/volto.po +55 -0
- package/locales/volto.pot +56 -1
- package/package.json +1 -1
- package/src/Blocks/EmbedTableauVisualization/Edit.jsx +1 -0
- package/src/Blocks/EmbedTableauVisualization/View.jsx +140 -43
- package/src/Blocks/EmbedTableauVisualization/__snapshots__/Edit.test.jsx.snap +90 -10
- package/src/Blocks/EmbedTableauVisualization/schema.js +44 -2
- package/src/Tableau/Tableau.jsx +114 -29
- package/src/Tableau/helpers.js +129 -1
- package/src/Views/VisualizationView.jsx +31 -19
- package/src/Widgets/CreatableSelectWidget.jsx +300 -0
- package/src/Widgets/VisualizationViewWidget.jsx +34 -18
- package/src/Widgets/VisualizationViewWidget.test.jsx +1 -0
- package/src/Widgets/VisualizationWidget.jsx +113 -29
- package/src/Widgets/VisualizationWidget.test.jsx +1 -1
- package/src/Widgets/index.js +2 -1
- package/src/Widgets/schema.js +314 -41
- package/src/index.js +6 -1
package/src/Tableau/helpers.js
CHANGED
|
@@ -1,4 +1,132 @@
|
|
|
1
|
-
import isUndefined from 'lodash
|
|
1
|
+
import { reduce, isUndefined, isString } from 'lodash';
|
|
2
|
+
import qs from 'querystring';
|
|
3
|
+
import { pickMetadata } from '@eeacms/volto-embed/helpers';
|
|
4
|
+
|
|
5
|
+
export function getQuery({
|
|
6
|
+
data_query = [],
|
|
7
|
+
location = {},
|
|
8
|
+
tableau_vis_url = '',
|
|
9
|
+
discodata_query = {},
|
|
10
|
+
}) {
|
|
11
|
+
return {
|
|
12
|
+
...reduce(
|
|
13
|
+
data_query,
|
|
14
|
+
(acc, { i, v }) => {
|
|
15
|
+
if (i && v.length) {
|
|
16
|
+
return { ...acc, [i]: v };
|
|
17
|
+
}
|
|
18
|
+
return acc;
|
|
19
|
+
},
|
|
20
|
+
{},
|
|
21
|
+
),
|
|
22
|
+
...(qs.parse(location.search?.replace('?', '')) || {}),
|
|
23
|
+
...(qs.parse(tableau_vis_url.split('?')[1]) || {}),
|
|
24
|
+
...(discodata_query?.search || {}),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function getTableauVisualization({
|
|
29
|
+
isBlock,
|
|
30
|
+
data,
|
|
31
|
+
tableauContent,
|
|
32
|
+
content,
|
|
33
|
+
}) {
|
|
34
|
+
const mergedContent =
|
|
35
|
+
(isBlock
|
|
36
|
+
? tableauContent
|
|
37
|
+
: {
|
|
38
|
+
...content,
|
|
39
|
+
tableau_visualization: {
|
|
40
|
+
...(content?.tableau_visualization || {}),
|
|
41
|
+
...pickMetadata(content),
|
|
42
|
+
},
|
|
43
|
+
}) || {};
|
|
44
|
+
const tableau_visualization =
|
|
45
|
+
mergedContent?.tableau_visualization || data?.tableau_visualization || {};
|
|
46
|
+
return {
|
|
47
|
+
...tableau_visualization,
|
|
48
|
+
...(isBlock ? pickMetadata(mergedContent) : {}),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function getParameters({ tableauVisualization, query, data }) {
|
|
53
|
+
const { urlParameters = [] } = tableauVisualization || {};
|
|
54
|
+
|
|
55
|
+
const staticParameters = [
|
|
56
|
+
...(tableauVisualization?.staticParameters || []),
|
|
57
|
+
...(data?.staticParameters || []),
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
...reduce(
|
|
62
|
+
staticParameters,
|
|
63
|
+
(acc, { field, value }) => {
|
|
64
|
+
if (field && value) {
|
|
65
|
+
return {
|
|
66
|
+
...acc,
|
|
67
|
+
[field]: value,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return acc;
|
|
71
|
+
},
|
|
72
|
+
{},
|
|
73
|
+
),
|
|
74
|
+
...reduce(
|
|
75
|
+
urlParameters,
|
|
76
|
+
(acc, { field, urlParam }) => {
|
|
77
|
+
if (field && query[urlParam]) {
|
|
78
|
+
return {
|
|
79
|
+
...acc,
|
|
80
|
+
[field]: isString(query[urlParam])
|
|
81
|
+
? query[urlParam].split(',')
|
|
82
|
+
: query[urlParam],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return acc;
|
|
86
|
+
},
|
|
87
|
+
{},
|
|
88
|
+
),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function getFilters({ tableauVisualization, query, data }) {
|
|
93
|
+
// const { dynamicFilters = [] } = tableauVisualization || {};
|
|
94
|
+
const staticFilters = [
|
|
95
|
+
...(tableauVisualization?.staticFilters || []),
|
|
96
|
+
...(data?.staticFilters || []),
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
...reduce(
|
|
101
|
+
staticFilters,
|
|
102
|
+
(acc, { field, value }) => {
|
|
103
|
+
if (field && value) {
|
|
104
|
+
return {
|
|
105
|
+
...acc,
|
|
106
|
+
[field]: value,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return acc;
|
|
110
|
+
},
|
|
111
|
+
{},
|
|
112
|
+
),
|
|
113
|
+
// ...reduce(
|
|
114
|
+
// dynamicFilters,
|
|
115
|
+
// (acc, { field, urlParam }) => {
|
|
116
|
+
// if (field && urlParam) {
|
|
117
|
+
// return {
|
|
118
|
+
// ...acc,
|
|
119
|
+
// [field]: isString(query[urlParam])
|
|
120
|
+
// ? query[urlParam].split(',')
|
|
121
|
+
// : query[urlParam],
|
|
122
|
+
// };
|
|
123
|
+
// }
|
|
124
|
+
// return acc;
|
|
125
|
+
// },
|
|
126
|
+
// {},
|
|
127
|
+
// ),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
2
130
|
|
|
3
131
|
export const getSheetnames = (viz) => {
|
|
4
132
|
if (!viz) return [];
|
|
@@ -1,25 +1,37 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import {
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { withRouter } from 'react-router';
|
|
3
3
|
import { Container } from 'semantic-ui-react';
|
|
4
|
+
import { hasBlocksData } from '@plone/volto/helpers';
|
|
4
5
|
import config from '@plone/volto/registry';
|
|
5
6
|
import RenderBlocks from '@plone/volto/components/theme/View/RenderBlocks';
|
|
6
|
-
import { pickMetadata } from '@eeacms/volto-embed/helpers';
|
|
7
7
|
import Tableau from '@eeacms/volto-tableau/Tableau/Tableau';
|
|
8
|
+
import {
|
|
9
|
+
getQuery,
|
|
10
|
+
getTableauVisualization,
|
|
11
|
+
getParameters,
|
|
12
|
+
getFilters,
|
|
13
|
+
} from '@eeacms/volto-tableau/Tableau/helpers';
|
|
8
14
|
|
|
9
15
|
const VisualizationView = (props) => {
|
|
10
|
-
const { content
|
|
11
|
-
|
|
12
|
-
const
|
|
16
|
+
const { location, content } = props;
|
|
17
|
+
|
|
18
|
+
const [tableauVisualization] = useState(() =>
|
|
19
|
+
getTableauVisualization({
|
|
20
|
+
isBlock: false,
|
|
21
|
+
content,
|
|
22
|
+
}),
|
|
23
|
+
);
|
|
24
|
+
const [query] = useState(() => {
|
|
25
|
+
return getQuery({ location });
|
|
26
|
+
});
|
|
13
27
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return options;
|
|
22
|
-
}, [staticParameters]);
|
|
28
|
+
const [extraParameters] = useState(() =>
|
|
29
|
+
getParameters({ tableauVisualization, query }),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const [extraFilters] = useState(() =>
|
|
33
|
+
getFilters({ tableauVisualization, query }),
|
|
34
|
+
);
|
|
23
35
|
|
|
24
36
|
return (
|
|
25
37
|
<Container id="page-document">
|
|
@@ -28,8 +40,7 @@ const VisualizationView = (props) => {
|
|
|
28
40
|
) : (
|
|
29
41
|
<Tableau
|
|
30
42
|
data={{
|
|
31
|
-
...
|
|
32
|
-
...pickMetadata(content),
|
|
43
|
+
...tableauVisualization,
|
|
33
44
|
with_notes: false,
|
|
34
45
|
with_sources: false,
|
|
35
46
|
with_more_info: false,
|
|
@@ -37,14 +48,15 @@ const VisualizationView = (props) => {
|
|
|
37
48
|
with_enlarge: true,
|
|
38
49
|
with_download: true,
|
|
39
50
|
}}
|
|
40
|
-
extraOptions={extraOptions}
|
|
41
51
|
breakpoints={
|
|
42
52
|
config.blocks.blocksConfig.embed_tableau_visualization.breakpoints
|
|
43
53
|
}
|
|
54
|
+
extraParameters={extraParameters}
|
|
55
|
+
extraFilters={extraFilters}
|
|
44
56
|
/>
|
|
45
57
|
)}
|
|
46
58
|
</Container>
|
|
47
59
|
);
|
|
48
60
|
};
|
|
49
61
|
|
|
50
|
-
export default VisualizationView;
|
|
62
|
+
export default withRouter(VisualizationView);
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CreatableSelectWidget component.
|
|
3
|
+
* @module components/manage/Widgets/CreatableSelectWidget
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { Component } from 'react';
|
|
7
|
+
import PropTypes from 'prop-types';
|
|
8
|
+
import { connect } from 'react-redux';
|
|
9
|
+
import { compose } from 'redux';
|
|
10
|
+
import { map } from 'lodash';
|
|
11
|
+
import { defineMessages, injectIntl } from 'react-intl';
|
|
12
|
+
import {
|
|
13
|
+
getVocabFromHint,
|
|
14
|
+
getVocabFromField,
|
|
15
|
+
getVocabFromItems,
|
|
16
|
+
} from '@plone/volto/helpers';
|
|
17
|
+
import { FormFieldWrapper } from '@plone/volto/components';
|
|
18
|
+
import { getVocabulary, getVocabularyTokenTitle } from '@plone/volto/actions';
|
|
19
|
+
import { normalizeValue } from '@plone/volto/components/manage/Widgets/SelectUtils';
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
customSelectStyles,
|
|
23
|
+
DropdownIndicator,
|
|
24
|
+
ClearIndicator,
|
|
25
|
+
Option,
|
|
26
|
+
selectTheme,
|
|
27
|
+
MenuList,
|
|
28
|
+
MultiValueContainer,
|
|
29
|
+
} from '@plone/volto/components/manage/Widgets/SelectStyling';
|
|
30
|
+
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
31
|
+
|
|
32
|
+
const messages = defineMessages({
|
|
33
|
+
default: {
|
|
34
|
+
id: 'Default',
|
|
35
|
+
defaultMessage: 'Default',
|
|
36
|
+
},
|
|
37
|
+
idTitle: {
|
|
38
|
+
id: 'Short Name',
|
|
39
|
+
defaultMessage: 'Short Name',
|
|
40
|
+
},
|
|
41
|
+
idDescription: {
|
|
42
|
+
id: 'Used for programmatic access to the fieldset.',
|
|
43
|
+
defaultMessage: 'Used for programmatic access to the fieldset.',
|
|
44
|
+
},
|
|
45
|
+
title: {
|
|
46
|
+
id: 'Title',
|
|
47
|
+
defaultMessage: 'Title',
|
|
48
|
+
},
|
|
49
|
+
description: {
|
|
50
|
+
id: 'Description',
|
|
51
|
+
defaultMessage: 'Description',
|
|
52
|
+
},
|
|
53
|
+
close: {
|
|
54
|
+
id: 'Close',
|
|
55
|
+
defaultMessage: 'Close',
|
|
56
|
+
},
|
|
57
|
+
choices: {
|
|
58
|
+
id: 'Choices',
|
|
59
|
+
defaultMessage: 'Choices',
|
|
60
|
+
},
|
|
61
|
+
required: {
|
|
62
|
+
id: 'Required',
|
|
63
|
+
defaultMessage: 'Required',
|
|
64
|
+
},
|
|
65
|
+
select: {
|
|
66
|
+
id: 'Select…',
|
|
67
|
+
defaultMessage: 'Select…',
|
|
68
|
+
},
|
|
69
|
+
no_value: {
|
|
70
|
+
id: 'No value',
|
|
71
|
+
defaultMessage: 'No value',
|
|
72
|
+
},
|
|
73
|
+
no_options: {
|
|
74
|
+
id: 'No options',
|
|
75
|
+
defaultMessage: 'No options',
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* CreatableSelectWidget component class.
|
|
81
|
+
* @function CreatableSelectWidget
|
|
82
|
+
* @returns {string} Markup of the component.
|
|
83
|
+
*/
|
|
84
|
+
class CreatableSelectWidget extends Component {
|
|
85
|
+
/**
|
|
86
|
+
* Property types.
|
|
87
|
+
* @property {Object} propTypes Property types.
|
|
88
|
+
* @static
|
|
89
|
+
*/
|
|
90
|
+
static propTypes = {
|
|
91
|
+
id: PropTypes.string.isRequired,
|
|
92
|
+
title: PropTypes.string.isRequired,
|
|
93
|
+
description: PropTypes.string,
|
|
94
|
+
required: PropTypes.bool,
|
|
95
|
+
error: PropTypes.arrayOf(PropTypes.string),
|
|
96
|
+
getVocabulary: PropTypes.func.isRequired,
|
|
97
|
+
getVocabularyTokenTitle: PropTypes.func.isRequired,
|
|
98
|
+
choices: PropTypes.arrayOf(
|
|
99
|
+
PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
|
100
|
+
),
|
|
101
|
+
items: PropTypes.shape({
|
|
102
|
+
vocabulary: PropTypes.object,
|
|
103
|
+
}),
|
|
104
|
+
widgetOptions: PropTypes.shape({
|
|
105
|
+
vocabulary: PropTypes.object,
|
|
106
|
+
}),
|
|
107
|
+
value: PropTypes.oneOfType([
|
|
108
|
+
PropTypes.object,
|
|
109
|
+
PropTypes.string,
|
|
110
|
+
PropTypes.bool,
|
|
111
|
+
PropTypes.func,
|
|
112
|
+
PropTypes.array,
|
|
113
|
+
]),
|
|
114
|
+
onChange: PropTypes.func.isRequired,
|
|
115
|
+
onBlur: PropTypes.func,
|
|
116
|
+
onClick: PropTypes.func,
|
|
117
|
+
onEdit: PropTypes.func,
|
|
118
|
+
onDelete: PropTypes.func,
|
|
119
|
+
wrapped: PropTypes.bool,
|
|
120
|
+
noValueOption: PropTypes.bool,
|
|
121
|
+
customOptionStyling: PropTypes.any,
|
|
122
|
+
isMulti: PropTypes.bool,
|
|
123
|
+
creatable: PropTypes.bool,
|
|
124
|
+
placeholder: PropTypes.string,
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Default properties
|
|
129
|
+
* @property {Object} defaultProps Default properties.
|
|
130
|
+
* @static
|
|
131
|
+
*/
|
|
132
|
+
static defaultProps = {
|
|
133
|
+
description: null,
|
|
134
|
+
required: false,
|
|
135
|
+
items: {
|
|
136
|
+
vocabulary: null,
|
|
137
|
+
},
|
|
138
|
+
widgetOptions: {
|
|
139
|
+
vocabulary: null,
|
|
140
|
+
},
|
|
141
|
+
error: [],
|
|
142
|
+
choices: [],
|
|
143
|
+
value: null,
|
|
144
|
+
onChange: () => {},
|
|
145
|
+
onBlur: () => {},
|
|
146
|
+
onClick: () => {},
|
|
147
|
+
onEdit: null,
|
|
148
|
+
onDelete: null,
|
|
149
|
+
noValueOption: true,
|
|
150
|
+
customOptionStyling: null,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Component did mount
|
|
155
|
+
* @method componentDidMount
|
|
156
|
+
* @returns {undefined}
|
|
157
|
+
*/
|
|
158
|
+
componentDidMount() {
|
|
159
|
+
if (
|
|
160
|
+
(!this.props.choices || this.props.choices?.length === 0) &&
|
|
161
|
+
this.props.vocabBaseUrl
|
|
162
|
+
) {
|
|
163
|
+
this.props.getVocabulary({
|
|
164
|
+
vocabNameOrURL: this.props.vocabBaseUrl,
|
|
165
|
+
size: -1,
|
|
166
|
+
subrequest: this.props.lang,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Render method.
|
|
173
|
+
* @method render
|
|
174
|
+
* @returns {string} Markup for the component.
|
|
175
|
+
*/
|
|
176
|
+
render() {
|
|
177
|
+
const { id, choices, value, intl, onChange } = this.props;
|
|
178
|
+
const normalizedValue = normalizeValue(choices, value, intl);
|
|
179
|
+
// Make sure that both disabled and isDisabled (from the DX layout feat work)
|
|
180
|
+
const disabled = this.props.disabled || this.props.isDisabled;
|
|
181
|
+
const Select = this.props.creatable
|
|
182
|
+
? this.props.reactSelectCreateable.default
|
|
183
|
+
: this.props.reactSelect.default;
|
|
184
|
+
|
|
185
|
+
let options = this.props.vocabBaseUrl
|
|
186
|
+
? this.props.choices
|
|
187
|
+
: [
|
|
188
|
+
...map(choices, (option) => ({
|
|
189
|
+
value: option[0],
|
|
190
|
+
label:
|
|
191
|
+
// Fix "None" on the serializer, to remove when fixed in p.restapi
|
|
192
|
+
option[1] !== 'None' && option[1] ? option[1] : option[0],
|
|
193
|
+
})),
|
|
194
|
+
// Only set "no-value" option if there's no default in the field
|
|
195
|
+
// TODO: also if this.props.defaultValue?
|
|
196
|
+
...(this.props.noValueOption && !this.props.default
|
|
197
|
+
? [
|
|
198
|
+
{
|
|
199
|
+
label: this.props.intl.formatMessage(messages.no_value),
|
|
200
|
+
value: 'no-value',
|
|
201
|
+
},
|
|
202
|
+
]
|
|
203
|
+
: []),
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
const isMulti = this.props.isMulti
|
|
207
|
+
? this.props.isMulti
|
|
208
|
+
: id === 'roles' || id === 'groups' || this.props.type === 'array';
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<FormFieldWrapper {...this.props}>
|
|
212
|
+
<Select
|
|
213
|
+
id={`field-${id}`}
|
|
214
|
+
key={choices}
|
|
215
|
+
name={id}
|
|
216
|
+
menuShouldScrollIntoView={false}
|
|
217
|
+
isDisabled={disabled}
|
|
218
|
+
isSearchable={true}
|
|
219
|
+
className="react-select-container"
|
|
220
|
+
classNamePrefix="react-select"
|
|
221
|
+
isMulti={isMulti}
|
|
222
|
+
options={options}
|
|
223
|
+
styles={customSelectStyles}
|
|
224
|
+
theme={selectTheme}
|
|
225
|
+
components={{
|
|
226
|
+
...(options?.length > 25 && {
|
|
227
|
+
MenuList,
|
|
228
|
+
}),
|
|
229
|
+
MultiValueContainer,
|
|
230
|
+
DropdownIndicator,
|
|
231
|
+
ClearIndicator,
|
|
232
|
+
Option: this.props.customOptionStyling || Option,
|
|
233
|
+
}}
|
|
234
|
+
value={normalizedValue}
|
|
235
|
+
placeholder={
|
|
236
|
+
this.props.placeholder ??
|
|
237
|
+
this.props.intl.formatMessage(messages.select)
|
|
238
|
+
}
|
|
239
|
+
onChange={(selectedOption) => {
|
|
240
|
+
if (isMulti) {
|
|
241
|
+
return onChange(
|
|
242
|
+
id,
|
|
243
|
+
selectedOption.map((el) => el.value),
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
return onChange(
|
|
247
|
+
id,
|
|
248
|
+
selectedOption && selectedOption.value !== 'no-value'
|
|
249
|
+
? selectedOption.value
|
|
250
|
+
: undefined,
|
|
251
|
+
);
|
|
252
|
+
}}
|
|
253
|
+
isClearable
|
|
254
|
+
/>
|
|
255
|
+
</FormFieldWrapper>
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export const CreatableSelectWidgetComponent = injectIntl(CreatableSelectWidget);
|
|
261
|
+
|
|
262
|
+
export default compose(
|
|
263
|
+
injectLazyLibs(['reactSelect', 'reactSelectCreateable']),
|
|
264
|
+
connect(
|
|
265
|
+
(state, props) => {
|
|
266
|
+
const vocabBaseUrl = !props.choices
|
|
267
|
+
? getVocabFromHint(props) ||
|
|
268
|
+
getVocabFromField(props) ||
|
|
269
|
+
getVocabFromItems(props)
|
|
270
|
+
: '';
|
|
271
|
+
|
|
272
|
+
const vocabState =
|
|
273
|
+
state.vocabularies?.[vocabBaseUrl]?.subrequests?.[state.intl.locale];
|
|
274
|
+
|
|
275
|
+
// If the schema already has the choices in it, then do not try to get the vocab,
|
|
276
|
+
// even if there is one
|
|
277
|
+
if (props.choices) {
|
|
278
|
+
return {
|
|
279
|
+
choices: props.choices,
|
|
280
|
+
lang: state.intl.locale,
|
|
281
|
+
};
|
|
282
|
+
} else if (vocabState) {
|
|
283
|
+
return {
|
|
284
|
+
vocabBaseUrl,
|
|
285
|
+
choices: vocabState?.items ?? [],
|
|
286
|
+
lang: state.intl.locale,
|
|
287
|
+
};
|
|
288
|
+
// There is a moment that vocabState is not there yet, so we need to pass the
|
|
289
|
+
// vocabBaseUrl to the component.
|
|
290
|
+
} else if (vocabBaseUrl) {
|
|
291
|
+
return {
|
|
292
|
+
vocabBaseUrl,
|
|
293
|
+
lang: state.intl.locale,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
return { lang: state.intl.locale };
|
|
297
|
+
},
|
|
298
|
+
{ getVocabulary, getVocabularyTokenTitle },
|
|
299
|
+
),
|
|
300
|
+
)(CreatableSelectWidgetComponent);
|
|
@@ -1,27 +1,41 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
import { connect } from 'react-redux';
|
|
3
|
+
import { compose } from 'redux';
|
|
4
|
+
import { withRouter } from 'react-router';
|
|
3
5
|
import config from '@plone/volto/registry';
|
|
4
|
-
import { pickMetadata } from '@eeacms/volto-embed/helpers';
|
|
5
6
|
import Tableau from '@eeacms/volto-tableau/Tableau/Tableau';
|
|
7
|
+
import {
|
|
8
|
+
getQuery,
|
|
9
|
+
getTableauVisualization,
|
|
10
|
+
getParameters,
|
|
11
|
+
getFilters,
|
|
12
|
+
} from '@eeacms/volto-tableau/Tableau/helpers';
|
|
6
13
|
|
|
7
14
|
function VisualizationViewWidget(props) {
|
|
8
|
-
const {
|
|
15
|
+
const { location, content } = props;
|
|
9
16
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return
|
|
18
|
-
}
|
|
17
|
+
const [tableauVisualization] = useState(() =>
|
|
18
|
+
getTableauVisualization({
|
|
19
|
+
isBlock: false,
|
|
20
|
+
content,
|
|
21
|
+
}),
|
|
22
|
+
);
|
|
23
|
+
const [query] = useState(() => {
|
|
24
|
+
return getQuery({ location });
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const [extraParameters] = useState(() =>
|
|
28
|
+
getParameters({ tableauVisualization, query }),
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const [extraFilters] = useState(() =>
|
|
32
|
+
getFilters({ tableauVisualization, query }),
|
|
33
|
+
);
|
|
19
34
|
|
|
20
35
|
return (
|
|
21
36
|
<Tableau
|
|
22
37
|
data={{
|
|
23
|
-
...
|
|
24
|
-
...pickMetadata(props.content),
|
|
38
|
+
...tableauVisualization,
|
|
25
39
|
with_notes: false,
|
|
26
40
|
with_sources: false,
|
|
27
41
|
with_more_info: false,
|
|
@@ -29,14 +43,16 @@ function VisualizationViewWidget(props) {
|
|
|
29
43
|
with_enlarge: true,
|
|
30
44
|
with_download: true,
|
|
31
45
|
}}
|
|
32
|
-
extraOptions={extraOptions}
|
|
33
46
|
breakpoints={
|
|
34
47
|
config.blocks.blocksConfig?.embed_tableau_visualization?.breakpoints
|
|
35
48
|
}
|
|
49
|
+
extraParameters={extraParameters}
|
|
50
|
+
extraFilters={extraFilters}
|
|
36
51
|
/>
|
|
37
52
|
);
|
|
38
53
|
}
|
|
39
54
|
|
|
40
|
-
export default
|
|
41
|
-
|
|
42
|
-
)
|
|
55
|
+
export default compose(
|
|
56
|
+
withRouter,
|
|
57
|
+
connect((state) => ({ content: state?.content?.data })),
|
|
58
|
+
)(VisualizationViewWidget);
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { render } from '@testing-library/react';
|
|
3
3
|
import '@testing-library/jest-dom/extend-expect';
|
|
4
4
|
import { Provider } from 'react-redux';
|
|
5
|
+
|
|
5
6
|
import VisualizationViewWidget from './VisualizationViewWidget';
|
|
6
7
|
|
|
7
8
|
describe('VisualizationViewWidget', () => {
|