@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
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { withRouter } from 'react-router';
|
|
3
|
+
import { connect } from 'react-redux';
|
|
4
|
+
import { compose } from 'redux';
|
|
5
|
+
import { isEqual } from 'lodash';
|
|
2
6
|
import { Modal, Button, Grid } from 'semantic-ui-react';
|
|
3
7
|
import config from '@plone/volto/registry';
|
|
4
8
|
import { FormFieldWrapper, InlineForm } from '@plone/volto/components';
|
|
5
9
|
import Tableau from '@eeacms/volto-tableau/Tableau/Tableau';
|
|
6
10
|
import getSchema from './schema';
|
|
11
|
+
import {
|
|
12
|
+
getQuery,
|
|
13
|
+
getTableauVisualization,
|
|
14
|
+
getParameters,
|
|
15
|
+
getFilters,
|
|
16
|
+
} from '@eeacms/volto-tableau/Tableau/helpers';
|
|
7
17
|
|
|
8
18
|
import '@eeacms/volto-tableau/less/tableau.less';
|
|
9
19
|
|
|
10
20
|
const VisualizationWidget = (props) => {
|
|
21
|
+
const { location, content } = props;
|
|
11
22
|
const viz = React.useRef();
|
|
23
|
+
const [schema, setSchema] = React.useState(null);
|
|
12
24
|
const [vizState, setVizState] = React.useState({
|
|
13
25
|
loaded: false,
|
|
14
26
|
loading: false,
|
|
@@ -17,19 +29,27 @@ const VisualizationWidget = (props) => {
|
|
|
17
29
|
const [open, setOpen] = React.useState(false);
|
|
18
30
|
const [value, setValue] = React.useState(props.value);
|
|
19
31
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
const [tableauVisualization, setTableauVisualization] = useState(() =>
|
|
33
|
+
getTableauVisualization({
|
|
34
|
+
isBlock: false,
|
|
35
|
+
content: {
|
|
36
|
+
...content,
|
|
37
|
+
tableau_visualization: value,
|
|
38
|
+
},
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const [query, setQuery] = useState(() => {
|
|
43
|
+
return getQuery({ location });
|
|
44
|
+
});
|
|
23
45
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return options;
|
|
32
|
-
}, [value]);
|
|
46
|
+
const [extraParameters, setExtraParameters] = useState(() =>
|
|
47
|
+
getParameters({ tableauVisualization, query }),
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const [extraFilters, setExtraFilters] = useState(() =>
|
|
51
|
+
getFilters({ tableauVisualization, query }),
|
|
52
|
+
);
|
|
33
53
|
|
|
34
54
|
const handleApplyChanges = () => {
|
|
35
55
|
props.onChange(props.id, value);
|
|
@@ -41,6 +61,63 @@ const VisualizationWidget = (props) => {
|
|
|
41
61
|
setOpen(false);
|
|
42
62
|
};
|
|
43
63
|
|
|
64
|
+
React.useEffect(() => {
|
|
65
|
+
if (!open && !isEqual(props.value, value)) {
|
|
66
|
+
setValue(props.value);
|
|
67
|
+
}
|
|
68
|
+
}, [props.value, value, open]);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Update tableau visualization
|
|
72
|
+
*/
|
|
73
|
+
React.useEffect(() => {
|
|
74
|
+
setTableauVisualization(
|
|
75
|
+
getTableauVisualization({
|
|
76
|
+
isBlock: false,
|
|
77
|
+
content: {
|
|
78
|
+
...content,
|
|
79
|
+
tableau_visualization: value,
|
|
80
|
+
},
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
}, [content, value]);
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Update query
|
|
87
|
+
*/
|
|
88
|
+
React.useEffect(() => {
|
|
89
|
+
setQuery(
|
|
90
|
+
getQuery({
|
|
91
|
+
location,
|
|
92
|
+
}),
|
|
93
|
+
);
|
|
94
|
+
}, [location]);
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Update extra parameters
|
|
98
|
+
*/
|
|
99
|
+
React.useEffect(() => {
|
|
100
|
+
setExtraParameters(getParameters({ tableauVisualization, query }));
|
|
101
|
+
}, [tableauVisualization, query]);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Update extra filters
|
|
105
|
+
*/
|
|
106
|
+
React.useEffect(() => {
|
|
107
|
+
setExtraFilters(getFilters({ tableauVisualization, query }));
|
|
108
|
+
}, [tableauVisualization, query]);
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get schema
|
|
112
|
+
*/
|
|
113
|
+
React.useEffect(() => {
|
|
114
|
+
getSchema({ config, viz: viz.current, vizState, data: value }).then(
|
|
115
|
+
(schema) => {
|
|
116
|
+
setSchema(schema);
|
|
117
|
+
},
|
|
118
|
+
);
|
|
119
|
+
}, [vizState, value]);
|
|
120
|
+
|
|
44
121
|
return (
|
|
45
122
|
<FormFieldWrapper {...props}>
|
|
46
123
|
<Modal id="tableau-editor-modal" open={open}>
|
|
@@ -52,17 +129,19 @@ const VisualizationWidget = (props) => {
|
|
|
52
129
|
computer={4}
|
|
53
130
|
className="tableau-editor-column"
|
|
54
131
|
>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
132
|
+
{schema && (
|
|
133
|
+
<InlineForm
|
|
134
|
+
block={props.block}
|
|
135
|
+
schema={schema}
|
|
136
|
+
onChangeField={(id, fieldValue) => {
|
|
137
|
+
setValue((value) => ({
|
|
138
|
+
...value,
|
|
139
|
+
[id]: fieldValue,
|
|
140
|
+
}));
|
|
141
|
+
}}
|
|
142
|
+
formData={value}
|
|
143
|
+
/>
|
|
144
|
+
)}
|
|
66
145
|
</Grid.Column>
|
|
67
146
|
<Grid.Column
|
|
68
147
|
mobile={8}
|
|
@@ -73,7 +152,7 @@ const VisualizationWidget = (props) => {
|
|
|
73
152
|
<Tableau
|
|
74
153
|
ref={viz}
|
|
75
154
|
data={{
|
|
76
|
-
...
|
|
155
|
+
...tableauVisualization,
|
|
77
156
|
with_notes: false,
|
|
78
157
|
with_sources: false,
|
|
79
158
|
with_more_info: false,
|
|
@@ -86,7 +165,8 @@ const VisualizationWidget = (props) => {
|
|
|
86
165
|
config.blocks.blocksConfig?.embed_tableau_visualization
|
|
87
166
|
?.breakpoints
|
|
88
167
|
}
|
|
89
|
-
|
|
168
|
+
extraParameters={extraParameters}
|
|
169
|
+
extraFilters={extraFilters}
|
|
90
170
|
setVizState={setVizState}
|
|
91
171
|
onChangeBlock={(_, newValue) => {
|
|
92
172
|
setValue(newValue);
|
|
@@ -122,7 +202,7 @@ const VisualizationWidget = (props) => {
|
|
|
122
202
|
</div>
|
|
123
203
|
<Tableau
|
|
124
204
|
data={{
|
|
125
|
-
...
|
|
205
|
+
...tableauVisualization,
|
|
126
206
|
autoScale: true,
|
|
127
207
|
with_notes: false,
|
|
128
208
|
with_sources: false,
|
|
@@ -134,10 +214,14 @@ const VisualizationWidget = (props) => {
|
|
|
134
214
|
breakpoints={
|
|
135
215
|
config.blocks.blocksConfig?.embed_tableau_visualization?.breakpoints
|
|
136
216
|
}
|
|
137
|
-
|
|
217
|
+
extraParameters={extraParameters}
|
|
218
|
+
extraFilters={extraFilters}
|
|
138
219
|
/>
|
|
139
220
|
</FormFieldWrapper>
|
|
140
221
|
);
|
|
141
222
|
};
|
|
142
223
|
|
|
143
|
-
export default
|
|
224
|
+
export default compose(
|
|
225
|
+
withRouter,
|
|
226
|
+
connect((state) => ({ content: state?.content?.data })),
|
|
227
|
+
)(VisualizationWidget);
|
|
@@ -26,7 +26,7 @@ describe('VisualizationWidget', () => {
|
|
|
26
26
|
|
|
27
27
|
const { container } = render(
|
|
28
28
|
<Provider store={global.store}>
|
|
29
|
-
<VisualizationWidget {...data} />
|
|
29
|
+
<VisualizationWidget {...data} id={'1234'} title="Title" />
|
|
30
30
|
</Provider>,
|
|
31
31
|
);
|
|
32
32
|
expect(container.querySelector('.tableau-wrapper')).toBeInTheDocument();
|
package/src/Widgets/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import VisualizationWidget from './VisualizationWidget';
|
|
2
2
|
import VisualizationViewWidget from './VisualizationViewWidget';
|
|
3
|
+
import CreatableSelectWidget from './CreatableSelectWidget';
|
|
3
4
|
|
|
4
|
-
export { VisualizationWidget, VisualizationViewWidget };
|
|
5
|
+
export { VisualizationWidget, VisualizationViewWidget, CreatableSelectWidget };
|
package/src/Widgets/schema.js
CHANGED
|
@@ -1,41 +1,148 @@
|
|
|
1
|
+
import { find, includes } from 'lodash';
|
|
1
2
|
import {
|
|
2
3
|
getSheetnamesChoices,
|
|
3
4
|
canChangeVizData,
|
|
4
5
|
} from '@eeacms/volto-tableau/Tableau/helpers';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
]
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
async function getUrlParametersSchema({ viz, vizState, data }) {
|
|
8
|
+
const tableauParameters =
|
|
9
|
+
vizState.loaded && viz ? await viz.getWorkbook().getParametersAsync() : [];
|
|
10
|
+
|
|
11
|
+
const currentFields = (data.urlParameters || [])
|
|
12
|
+
.map((p) => p.field)
|
|
13
|
+
.filter((f) => f);
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
title: 'Parameter',
|
|
17
|
+
fieldsets: [
|
|
18
|
+
{ id: 'default', title: 'Default', fields: ['field', 'urlParam'] },
|
|
19
|
+
],
|
|
20
|
+
properties: {
|
|
21
|
+
field: {
|
|
22
|
+
title: 'Tableau parameter',
|
|
23
|
+
widget: 'creatable_select',
|
|
24
|
+
isMulti: false,
|
|
25
|
+
creatable: true,
|
|
26
|
+
choices: tableauParameters
|
|
27
|
+
.filter((p) => {
|
|
28
|
+
return !includes(currentFields, p.getName());
|
|
29
|
+
})
|
|
30
|
+
.map((p) => [p.getName(), p.getName()]),
|
|
31
|
+
},
|
|
32
|
+
urlParam: {
|
|
33
|
+
title: 'Field name',
|
|
34
|
+
type: 'text',
|
|
35
|
+
},
|
|
15
36
|
},
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
37
|
+
required: [],
|
|
38
|
+
tableauParameters,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function getStaticParametersSchema({ viz, vizState, data }) {
|
|
43
|
+
const tableauParameters =
|
|
44
|
+
vizState.loaded && viz ? await viz.getWorkbook().getParametersAsync() : [];
|
|
45
|
+
|
|
46
|
+
const currentFields = (data.staticParameters || [])
|
|
47
|
+
.map((p) => p.field)
|
|
48
|
+
.filter((f) => f);
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
title: 'Parameter',
|
|
52
|
+
fieldsets: [{ id: 'default', title: 'Default', fields: ['field'] }],
|
|
53
|
+
properties: {
|
|
54
|
+
field: {
|
|
55
|
+
title: 'Tableau parameter',
|
|
56
|
+
widget: 'creatable_select',
|
|
57
|
+
isMulti: false,
|
|
58
|
+
creatable: true,
|
|
59
|
+
choices: tableauParameters
|
|
60
|
+
.filter((p) => {
|
|
61
|
+
return !includes(currentFields, p.getName());
|
|
62
|
+
})
|
|
63
|
+
.map((p) => [p.getName(), p.getName()]),
|
|
64
|
+
},
|
|
65
|
+
value: {
|
|
66
|
+
title: 'Value',
|
|
67
|
+
type: 'text',
|
|
68
|
+
},
|
|
19
69
|
},
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
};
|
|
70
|
+
required: [],
|
|
71
|
+
tableauParameters,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
23
74
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
75
|
+
async function getDynamicFiltersSchema({ viz, vizState, data }) {
|
|
76
|
+
const tableauFilters =
|
|
77
|
+
vizState.loaded && viz
|
|
78
|
+
? await viz.getWorkbook().getActiveSheet().getFiltersAsync()
|
|
79
|
+
: [];
|
|
80
|
+
|
|
81
|
+
const currentFields = (data.staticFilters || [])
|
|
82
|
+
.map((p) => p.field)
|
|
83
|
+
.filter((f) => f);
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
title: 'Filter',
|
|
87
|
+
fieldsets: [
|
|
88
|
+
{ id: 'default', title: 'Default', fields: ['field', 'urlParam'] },
|
|
89
|
+
],
|
|
90
|
+
properties: {
|
|
91
|
+
field: {
|
|
92
|
+
title: 'Tableau filter',
|
|
93
|
+
widget: 'creatable_select',
|
|
94
|
+
isMulti: false,
|
|
95
|
+
creatable: true,
|
|
96
|
+
choices: tableauFilters
|
|
97
|
+
.filter((p) => {
|
|
98
|
+
return !includes(currentFields, p.getFieldName());
|
|
99
|
+
})
|
|
100
|
+
.map((p) => [p.getFieldName(), p.getFieldName()]),
|
|
101
|
+
},
|
|
102
|
+
urlParam: {
|
|
103
|
+
title: 'Field name',
|
|
104
|
+
type: 'text',
|
|
105
|
+
},
|
|
31
106
|
},
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
107
|
+
required: [],
|
|
108
|
+
tableauFilters,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function getStaticFiltersSchema({ viz, vizState, data }) {
|
|
113
|
+
const tableauFilters =
|
|
114
|
+
vizState.loaded && viz
|
|
115
|
+
? await viz.getWorkbook().getActiveSheet().getFiltersAsync()
|
|
116
|
+
: [];
|
|
117
|
+
|
|
118
|
+
const currentFields = (data.staticFilters || [])
|
|
119
|
+
.map((p) => p.field)
|
|
120
|
+
.filter((f) => f);
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
title: 'Filter',
|
|
124
|
+
fieldsets: [{ id: 'default', title: 'Default', fields: ['field'] }],
|
|
125
|
+
properties: {
|
|
126
|
+
field: {
|
|
127
|
+
title: 'Tableau filter',
|
|
128
|
+
widget: 'creatable_select',
|
|
129
|
+
isMulti: false,
|
|
130
|
+
creatable: true,
|
|
131
|
+
choices: tableauFilters
|
|
132
|
+
.filter((p) => {
|
|
133
|
+
return !includes(currentFields, p.getFieldName());
|
|
134
|
+
})
|
|
135
|
+
.map((p) => [p.getFieldName(), p.getFieldName()]),
|
|
136
|
+
},
|
|
137
|
+
value: {
|
|
138
|
+
title: 'Value',
|
|
139
|
+
type: 'text',
|
|
140
|
+
},
|
|
35
141
|
},
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
};
|
|
142
|
+
required: [],
|
|
143
|
+
tableauFilters,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
39
146
|
|
|
40
147
|
const breakpointUrlSchema = (config) => {
|
|
41
148
|
const breakpoints =
|
|
@@ -61,7 +168,7 @@ const breakpointUrlSchema = (config) => {
|
|
|
61
168
|
};
|
|
62
169
|
};
|
|
63
170
|
|
|
64
|
-
export default (config, viz, vizState) => {
|
|
171
|
+
export default async ({ config, viz, vizState, data }) => {
|
|
65
172
|
const isDisabled = !canChangeVizData(viz, vizState);
|
|
66
173
|
|
|
67
174
|
return {
|
|
@@ -81,12 +188,18 @@ export default (config, viz, vizState) => {
|
|
|
81
188
|
'hideToolbar',
|
|
82
189
|
'autoScale',
|
|
83
190
|
'toolbarPosition',
|
|
191
|
+
'breakpointUrls',
|
|
84
192
|
],
|
|
85
193
|
},
|
|
86
194
|
{
|
|
87
|
-
id: '
|
|
88
|
-
title: '
|
|
89
|
-
fields: ['urlParameters', 'staticParameters'
|
|
195
|
+
id: 'parameters',
|
|
196
|
+
title: 'Parameters',
|
|
197
|
+
fields: ['urlParameters', 'staticParameters'],
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
id: 'filters',
|
|
201
|
+
title: 'Filters',
|
|
202
|
+
fields: ['staticFilters'],
|
|
90
203
|
},
|
|
91
204
|
],
|
|
92
205
|
properties: {
|
|
@@ -125,22 +238,182 @@ export default (config, viz, vizState) => {
|
|
|
125
238
|
isDisabled,
|
|
126
239
|
},
|
|
127
240
|
urlParameters: {
|
|
128
|
-
title: '
|
|
241
|
+
title: 'Dynamic parameters',
|
|
129
242
|
widget: 'object_list',
|
|
130
|
-
schema:
|
|
131
|
-
description:
|
|
243
|
+
schema: await getUrlParametersSchema({ viz, vizState, data }),
|
|
244
|
+
description: (
|
|
245
|
+
<div style={{ color: 'rgb(15, 130, 204)', fontWeight: '400' }}>
|
|
246
|
+
<p>Set a list of dynamic parameters that can be used as:</p>
|
|
247
|
+
<ul>
|
|
248
|
+
<li style={{ marginBottom: '0.5rem' }}>
|
|
249
|
+
<p style={{ marginBottom: '0' }}>
|
|
250
|
+
URL parameters - when you embed this tableau dashboard you can
|
|
251
|
+
also add query parameters defined bellow.
|
|
252
|
+
</p>
|
|
253
|
+
<p style={{ marginBottom: '0.25rem' }}>
|
|
254
|
+
E.g: When you embed this tableau dashboard you can define the
|
|
255
|
+
url as:
|
|
256
|
+
</p>
|
|
257
|
+
<p>
|
|
258
|
+
<span>
|
|
259
|
+
/path/to/embeded/tableau/visualization?
|
|
260
|
+
<span
|
|
261
|
+
style={{ color: 'rgb(218, 1, 45)' }}
|
|
262
|
+
>{`{field_name}`}</span>
|
|
263
|
+
=Romania
|
|
264
|
+
</span>
|
|
265
|
+
</p>
|
|
266
|
+
</li>
|
|
267
|
+
<li>
|
|
268
|
+
<p style={{ marginBottom: '0' }}>
|
|
269
|
+
Context data query - when you create a content where the
|
|
270
|
+
tableau dashboard will be embeded you can add data query to
|
|
271
|
+
that content.
|
|
272
|
+
</p>
|
|
273
|
+
<p>
|
|
274
|
+
NOTE: the content type should have the{' '}
|
|
275
|
+
<span style={{ color: 'rgb(218, 1, 45)' }}>
|
|
276
|
+
'Parameters for data connection'
|
|
277
|
+
</span>{' '}
|
|
278
|
+
behavior enabled
|
|
279
|
+
</p>
|
|
280
|
+
</li>
|
|
281
|
+
</ul>
|
|
282
|
+
</div>
|
|
283
|
+
),
|
|
132
284
|
isDisabled,
|
|
133
285
|
},
|
|
134
286
|
staticParameters: {
|
|
135
287
|
title: 'Static parameters',
|
|
136
288
|
widget: 'object_list',
|
|
137
|
-
schema:
|
|
289
|
+
schema: await getStaticParametersSchema({ viz, vizState, data }),
|
|
290
|
+
schemaExtender: (schema, data) => {
|
|
291
|
+
const tableauParameter = find(
|
|
292
|
+
schema.tableauParameters,
|
|
293
|
+
(p) => p.getName() === data.field,
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
schema.fieldsets[0].fields = ['field', 'value'];
|
|
297
|
+
|
|
298
|
+
if (!data.field || !tableauParameter) {
|
|
299
|
+
return schema;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const allowableValuesType = tableauParameter.getAllowableValuesType();
|
|
303
|
+
const dataType = tableauParameter.getDataType();
|
|
304
|
+
|
|
305
|
+
if (includes(['all', 'list'], allowableValuesType)) {
|
|
306
|
+
schema.properties.value.choices =
|
|
307
|
+
tableauParameter
|
|
308
|
+
.getAllowableValues()
|
|
309
|
+
?.map((v) => [v.value, v.formattedValue]) || [];
|
|
310
|
+
schema.properties.value.description = 'Select a value';
|
|
311
|
+
}
|
|
312
|
+
if (allowableValuesType === 'all') {
|
|
313
|
+
schema.properties.value.isMulti = true;
|
|
314
|
+
schema.properties.value.description = 'Select multiple values';
|
|
315
|
+
}
|
|
316
|
+
if (allowableValuesType === 'range' && dataType === 'date') {
|
|
317
|
+
schema.properties.value.widget = 'date';
|
|
318
|
+
} else if (allowableValuesType === 'range') {
|
|
319
|
+
schema.properties.value.type = 'number';
|
|
320
|
+
schema.properties.value.step = tableauParameter.getStepSize();
|
|
321
|
+
}
|
|
322
|
+
return schema;
|
|
323
|
+
},
|
|
324
|
+
description: (
|
|
325
|
+
<div style={{ color: 'rgb(15, 130, 204)', fontWeight: '400' }}>
|
|
326
|
+
<p style={{ marginBottom: '0' }}>
|
|
327
|
+
Set a list of static parameters.
|
|
328
|
+
</p>
|
|
329
|
+
</div>
|
|
330
|
+
),
|
|
331
|
+
isDisabled,
|
|
332
|
+
},
|
|
333
|
+
dynamicFilters: {
|
|
334
|
+
title: 'Dynamic filters',
|
|
335
|
+
widget: 'object_list',
|
|
336
|
+
schema: await getDynamicFiltersSchema({ viz, vizState, data }),
|
|
337
|
+
description: (
|
|
338
|
+
<div style={{ color: 'rgb(15, 130, 204)', fontWeight: '400' }}>
|
|
339
|
+
<p>Set a list of dynamic filters that can be used as:</p>
|
|
340
|
+
<ul>
|
|
341
|
+
<li style={{ marginBottom: '0.5rem' }}>
|
|
342
|
+
<p style={{ marginBottom: '0' }}>
|
|
343
|
+
URL parameters - when you embed this tableau dashboard you can
|
|
344
|
+
also add filters defined bellow as query parameters.
|
|
345
|
+
</p>
|
|
346
|
+
<p style={{ marginBottom: '0.25rem' }}>
|
|
347
|
+
E.g: When you embed this tableau dashboard you can define the
|
|
348
|
+
url as:
|
|
349
|
+
</p>
|
|
350
|
+
<p>
|
|
351
|
+
<span>
|
|
352
|
+
/path/to/embeded/tableau/visualization?
|
|
353
|
+
<span
|
|
354
|
+
style={{ color: 'rgb(218, 1, 45)' }}
|
|
355
|
+
>{`{field_name}`}</span>
|
|
356
|
+
=Agriculture,Forestry and Fishing
|
|
357
|
+
</span>
|
|
358
|
+
</p>
|
|
359
|
+
</li>
|
|
360
|
+
<li>
|
|
361
|
+
<p style={{ marginBottom: '0' }}>
|
|
362
|
+
Context data query - when you create a content where the
|
|
363
|
+
tableau dashboard will be embeded you can add data query to
|
|
364
|
+
that content.
|
|
365
|
+
</p>
|
|
366
|
+
<p>
|
|
367
|
+
NOTE: the content type should have the{' '}
|
|
368
|
+
<span style={{ color: 'rgb(218, 1, 45)' }}>
|
|
369
|
+
'Parameters for data connection'
|
|
370
|
+
</span>{' '}
|
|
371
|
+
behavior enabled
|
|
372
|
+
</p>
|
|
373
|
+
</li>
|
|
374
|
+
</ul>
|
|
375
|
+
</div>
|
|
376
|
+
),
|
|
377
|
+
isDisabled,
|
|
378
|
+
},
|
|
379
|
+
staticFilters: {
|
|
380
|
+
title: 'Static filters',
|
|
381
|
+
widget: 'object_list',
|
|
382
|
+
schema: await getStaticFiltersSchema({ viz, vizState, data }),
|
|
383
|
+
schemaExtender: (schema, data) => {
|
|
384
|
+
const tableauFilters = find(
|
|
385
|
+
schema.tableauFilters,
|
|
386
|
+
(p) => p.getFieldName() === data.field,
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
schema.fieldsets[0].fields = ['field', 'value'];
|
|
390
|
+
|
|
391
|
+
if (!data.field || !tableauFilters) {
|
|
392
|
+
return schema;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const filterType = tableauFilters.getFilterType();
|
|
396
|
+
|
|
397
|
+
if (includes(['categorical'], filterType)) {
|
|
398
|
+
schema.properties.value.choices =
|
|
399
|
+
tableauFilters
|
|
400
|
+
.getAppliedValues()
|
|
401
|
+
?.map((v) => [v.value, v.formattedValue]) || [];
|
|
402
|
+
schema.properties.value.isMulti = true;
|
|
403
|
+
schema.fieldsets[0].fields = ['field', 'value'];
|
|
404
|
+
} else {
|
|
405
|
+
schema.properties.field.description = (
|
|
406
|
+
<span style={{ color: 'rgb(218, 1, 45)' }}>
|
|
407
|
+
Filter type ({filterType}) not supported
|
|
408
|
+
</span>
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
return schema;
|
|
412
|
+
},
|
|
138
413
|
description: (
|
|
139
|
-
|
|
140
|
-
Set a list of static
|
|
141
|
-
|
|
142
|
-
<b>NOTE: You need to trigger a refresh for this to take effect</b>
|
|
143
|
-
</>
|
|
414
|
+
<div style={{ color: 'rgb(15, 130, 204)', fontWeight: '400' }}>
|
|
415
|
+
<p style={{ marginBottom: '0' }}>Set a list of static filters.</p>
|
|
416
|
+
</div>
|
|
144
417
|
),
|
|
145
418
|
isDisabled,
|
|
146
419
|
},
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import installBlocks from './Blocks';
|
|
2
2
|
import { VisualizationView } from './Views';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
VisualizationWidget,
|
|
5
|
+
VisualizationViewWidget,
|
|
6
|
+
CreatableSelectWidget,
|
|
7
|
+
} from './Widgets';
|
|
4
8
|
|
|
5
9
|
const applyConfig = (config) => {
|
|
6
10
|
config.settings.allowed_cors_destinations = [
|
|
@@ -13,6 +17,7 @@ const applyConfig = (config) => {
|
|
|
13
17
|
config.views.contentTypesViews.tableau_visualization = VisualizationView;
|
|
14
18
|
config.widgets.id.tableau_visualization = VisualizationWidget;
|
|
15
19
|
config.widgets.views.id.tableau_visualization = VisualizationViewWidget;
|
|
20
|
+
config.widgets.widget.creatable_select = CreatableSelectWidget;
|
|
16
21
|
|
|
17
22
|
return [installBlocks].reduce((acc, apply) => apply(acc), config);
|
|
18
23
|
};
|