@eeacms/volto-tableau 1.3.0 → 3.0.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/.coverage.babel.config.js +1 -1
- package/.i18n.babel.config.js +1 -0
- package/.project.eslintrc.js +47 -0
- package/CHANGELOG.md +35 -1
- package/Jenkinsfile +10 -10
- package/babel.config.js +17 -0
- package/cypress/{integration/block-basics.js → e2e/block-basics.cy.js} +8 -11
- package/cypress/support/commands.js +223 -1
- package/cypress/support/e2e.js +125 -0
- package/cypress.config.js +26 -0
- package/jest-addon.config.js +3 -4
- package/package.json +4 -2
- package/src/Blocks/EmbedEEATableauBlock/Edit.jsx +55 -0
- package/src/Blocks/EmbedEEATableauBlock/View.jsx +55 -0
- package/src/Blocks/EmbedEEATableauBlock/schema.js +31 -0
- package/src/ConnectedTableau/ConnectedTableau.jsx +21 -0
- package/src/Tableau/View.jsx +16 -2
- package/src/TableauBlock/View.jsx +5 -0
- package/src/Views/VisualizationView.jsx +43 -0
- package/src/Views/index.js +3 -0
- package/src/Widgets/VisualizationWidget.jsx +158 -0
- package/src/Widgets/index.js +3 -0
- package/src/Widgets/schema.js +176 -0
- package/src/Widgets/style.less +8 -0
- package/src/helpers.js +11 -4
- package/src/index.js +29 -0
- package/src/less/tableau.less +55 -0
- package/cypress/plugins/index.js +0 -26
- package/cypress/support/index.js +0 -53
- package/cypress.json +0 -17
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import BlockDataForm from '@plone/volto/components/manage/Form/BlockDataForm';
|
|
3
|
+
import { SidebarPortal } from '@plone/volto/components';
|
|
4
|
+
import { getContent } from '@plone/volto/actions';
|
|
5
|
+
import View from './View';
|
|
6
|
+
import Schema from './schema';
|
|
7
|
+
import { connect } from 'react-redux';
|
|
8
|
+
import { compose } from 'redux';
|
|
9
|
+
|
|
10
|
+
const Edit = (props) => {
|
|
11
|
+
const { data, block, onChangeBlock, id } = props;
|
|
12
|
+
const schema = React.useMemo(() => Schema(props), [props]);
|
|
13
|
+
|
|
14
|
+
React.useEffect(() => {
|
|
15
|
+
if (!Object.hasOwn(data, 'show_sources')) {
|
|
16
|
+
onChangeBlock(block, {
|
|
17
|
+
...data,
|
|
18
|
+
show_sources: true,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}, [block, data, onChangeBlock]);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<>
|
|
25
|
+
<View data={data} id={id} />
|
|
26
|
+
<SidebarPortal selected={props.selected}>
|
|
27
|
+
<BlockDataForm
|
|
28
|
+
block={block}
|
|
29
|
+
title={schema.title}
|
|
30
|
+
schema={schema}
|
|
31
|
+
onChangeField={(id, value) => {
|
|
32
|
+
props.onChangeBlock(block, {
|
|
33
|
+
...data,
|
|
34
|
+
[id]: value,
|
|
35
|
+
});
|
|
36
|
+
}}
|
|
37
|
+
formData={data}
|
|
38
|
+
/>
|
|
39
|
+
</SidebarPortal>
|
|
40
|
+
</>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default compose(
|
|
45
|
+
connect(
|
|
46
|
+
(state, props) => ({
|
|
47
|
+
block_data: state.content.data,
|
|
48
|
+
data_provenance:
|
|
49
|
+
state.content.subrequests?.[props.id]?.data?.data_provenance,
|
|
50
|
+
}),
|
|
51
|
+
{
|
|
52
|
+
getContent,
|
|
53
|
+
},
|
|
54
|
+
),
|
|
55
|
+
)(Edit);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ConnectedTableau from '../../ConnectedTableau/ConnectedTableau';
|
|
3
|
+
|
|
4
|
+
import { getContent } from '@plone/volto/actions';
|
|
5
|
+
|
|
6
|
+
import { connect } from 'react-redux';
|
|
7
|
+
import { compose } from 'redux';
|
|
8
|
+
|
|
9
|
+
const View = (props) => {
|
|
10
|
+
const { data } = props || {};
|
|
11
|
+
const { vis_url = '' } = data;
|
|
12
|
+
const show_sources = data?.show_sources ?? false;
|
|
13
|
+
|
|
14
|
+
React.useEffect(() => {
|
|
15
|
+
if (vis_url) {
|
|
16
|
+
props.getContent(vis_url, null, props.id);
|
|
17
|
+
}
|
|
18
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
19
|
+
}, [vis_url]);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
{data?.vis_url ? (
|
|
24
|
+
<>
|
|
25
|
+
<ConnectedTableau {...props.tableau_visualization} id={props.id} />
|
|
26
|
+
{show_sources &&
|
|
27
|
+
data.tableauSources &&
|
|
28
|
+
props.tableau_visualization ? (
|
|
29
|
+
''
|
|
30
|
+
) : show_sources ? (
|
|
31
|
+
<div>Data provenance is not set in the visualization</div>
|
|
32
|
+
) : (
|
|
33
|
+
''
|
|
34
|
+
)}
|
|
35
|
+
</>
|
|
36
|
+
) : (
|
|
37
|
+
<div>Please select a visualization from block editor.</div>
|
|
38
|
+
)}
|
|
39
|
+
</>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default compose(
|
|
44
|
+
connect(
|
|
45
|
+
(state, props) => ({
|
|
46
|
+
data_provenance:
|
|
47
|
+
state.content.subrequests?.[props.id]?.data?.data_provenance,
|
|
48
|
+
tableau_visualization:
|
|
49
|
+
state.content.subrequests?.[props.id]?.data?.tableau_visualization_data,
|
|
50
|
+
}),
|
|
51
|
+
{
|
|
52
|
+
getContent,
|
|
53
|
+
},
|
|
54
|
+
),
|
|
55
|
+
)(View);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const Schema = (props) => {
|
|
2
|
+
return {
|
|
3
|
+
title: 'Embed EEA Tableau',
|
|
4
|
+
fieldsets: [
|
|
5
|
+
{
|
|
6
|
+
id: 'default',
|
|
7
|
+
title: 'Default',
|
|
8
|
+
fields: ['vis_url', 'height', 'show_sources'],
|
|
9
|
+
},
|
|
10
|
+
],
|
|
11
|
+
properties: {
|
|
12
|
+
vis_url: {
|
|
13
|
+
widget: 'object_by_path',
|
|
14
|
+
title: 'Visualization',
|
|
15
|
+
},
|
|
16
|
+
height: {
|
|
17
|
+
title: 'Height',
|
|
18
|
+
type: 'number',
|
|
19
|
+
default: 450,
|
|
20
|
+
},
|
|
21
|
+
show_sources: {
|
|
22
|
+
title: 'Toggle sources',
|
|
23
|
+
type: 'boolean',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
required: ['vis_url'],
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default Schema;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Tableau from '@eeacms/volto-tableau/Tableau/View';
|
|
3
|
+
|
|
4
|
+
const ConnectedTableau = (props) => {
|
|
5
|
+
const [error, setError] = React.useState(null);
|
|
6
|
+
const [loaded, setLoaded] = React.useState(null);
|
|
7
|
+
return (
|
|
8
|
+
<div className="tableau-block">
|
|
9
|
+
<Tableau
|
|
10
|
+
error={error}
|
|
11
|
+
loaded={loaded}
|
|
12
|
+
setError={setError}
|
|
13
|
+
setLoaded={setLoaded}
|
|
14
|
+
data={{ ...props?.general, ...props?.options, ...props?.extraOptions }}
|
|
15
|
+
url={props?.general?.url}
|
|
16
|
+
/>
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default ConnectedTableau;
|
package/src/Tableau/View.jsx
CHANGED
|
@@ -5,7 +5,7 @@ import { toast } from 'react-toastify';
|
|
|
5
5
|
import { Toast } from '@plone/volto/components';
|
|
6
6
|
import { setTableauApi } from '@eeacms/volto-tableau/actions';
|
|
7
7
|
import cx from 'classnames';
|
|
8
|
-
import {
|
|
8
|
+
import { loadTableauScript } from '../helpers';
|
|
9
9
|
|
|
10
10
|
const Tableau = (props) => {
|
|
11
11
|
const ref = React.useRef(null);
|
|
@@ -36,7 +36,7 @@ const Tableau = (props) => {
|
|
|
36
36
|
const url = props.url || defaultUrl;
|
|
37
37
|
|
|
38
38
|
//load tableau from script tag
|
|
39
|
-
const tableau =
|
|
39
|
+
const tableau = loadTableauScript(() => {}, version);
|
|
40
40
|
|
|
41
41
|
const onFilterChange = (filter) => {
|
|
42
42
|
const newFilters = { ...filters.current };
|
|
@@ -186,6 +186,7 @@ const Tableau = (props) => {
|
|
|
186
186
|
tableau,
|
|
187
187
|
toolbarPosition,
|
|
188
188
|
url,
|
|
189
|
+
version,
|
|
189
190
|
]);
|
|
190
191
|
|
|
191
192
|
React.useEffect(() => {
|
|
@@ -205,6 +206,19 @@ const Tableau = (props) => {
|
|
|
205
206
|
return (
|
|
206
207
|
<div id="tableau-wrap">
|
|
207
208
|
<div id="tableau-outer">
|
|
209
|
+
{data && Object.keys(data).length > 0 ? (
|
|
210
|
+
<>
|
|
211
|
+
{loaded ? (
|
|
212
|
+
''
|
|
213
|
+
) : (
|
|
214
|
+
<div className="tableau-loader">
|
|
215
|
+
<span>Loading Tableau v{version}</span>
|
|
216
|
+
</div>
|
|
217
|
+
)}
|
|
218
|
+
</>
|
|
219
|
+
) : (
|
|
220
|
+
<div>No data present in that visualization.</div>
|
|
221
|
+
)}
|
|
208
222
|
<div
|
|
209
223
|
className={cx('tableau', version, {
|
|
210
224
|
'tableau-scale': autoScale,
|
|
@@ -47,6 +47,11 @@ const View = (props) => {
|
|
|
47
47
|
/* eslint-disable-next-line */
|
|
48
48
|
}, []);
|
|
49
49
|
|
|
50
|
+
React.useEffect(() => {
|
|
51
|
+
if (props.setTableauError) props.setTableauError(error);
|
|
52
|
+
/* eslint-disable-next-line */
|
|
53
|
+
}, [error]);
|
|
54
|
+
|
|
50
55
|
React.useEffect(() => {
|
|
51
56
|
const newExtraFilters = { ...extraFilters };
|
|
52
57
|
urlParameters.forEach((element) => {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import TableauView from '../TableauBlock/View';
|
|
3
|
+
|
|
4
|
+
const VisualizationView = (props) => {
|
|
5
|
+
const [tableauError, setTableauError] = React.useState('');
|
|
6
|
+
const { content = {} } = props;
|
|
7
|
+
const { tableau_visualization_data = {} } = content;
|
|
8
|
+
|
|
9
|
+
const TableauNotDisplayed = () => {
|
|
10
|
+
return (
|
|
11
|
+
<div className="tableau-block not_displayed_tableau">
|
|
12
|
+
<div className="tableau-info">
|
|
13
|
+
{!tableau_visualization_data.general?.url ? (
|
|
14
|
+
<p className="tableau-error">URL required</p>
|
|
15
|
+
) : tableauError ? (
|
|
16
|
+
<p className="tableau-error">{tableauError}</p>
|
|
17
|
+
) : (
|
|
18
|
+
''
|
|
19
|
+
)}
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
return (
|
|
25
|
+
<div>
|
|
26
|
+
{!tableau_visualization_data.general?.url || tableauError ? (
|
|
27
|
+
<TableauNotDisplayed />
|
|
28
|
+
) : (
|
|
29
|
+
''
|
|
30
|
+
)}
|
|
31
|
+
<TableauView
|
|
32
|
+
setTableauError={setTableauError}
|
|
33
|
+
data={{
|
|
34
|
+
...tableau_visualization_data.general,
|
|
35
|
+
...tableau_visualization_data.options,
|
|
36
|
+
...tableau_visualization_data.extraOptions,
|
|
37
|
+
}}
|
|
38
|
+
/>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default VisualizationView;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Modal, Button, Grid } from 'semantic-ui-react';
|
|
3
|
+
import '@eeacms/volto-tableau/less/tableau.less';
|
|
4
|
+
import config from '@plone/volto/registry';
|
|
5
|
+
|
|
6
|
+
import { FormFieldWrapper, InlineForm } from '@plone/volto/components';
|
|
7
|
+
|
|
8
|
+
import TableauView from '../TableauBlock/View';
|
|
9
|
+
import Schema from './schema';
|
|
10
|
+
|
|
11
|
+
const VisualizationWidget = (props) => {
|
|
12
|
+
const [open, setOpen] = React.useState(false);
|
|
13
|
+
const { onChange = {}, id } = props;
|
|
14
|
+
|
|
15
|
+
const block = React.useMemo(() => props.block, [props.block]);
|
|
16
|
+
const value = React.useMemo(() => props.value, [props.value]);
|
|
17
|
+
|
|
18
|
+
const [intValue, setIntValue] = React.useState(value);
|
|
19
|
+
const [tableauError, setTableauError] = React.useState('');
|
|
20
|
+
|
|
21
|
+
const dataForm = { tableau_data: intValue };
|
|
22
|
+
|
|
23
|
+
const handleApplyChanges = () => {
|
|
24
|
+
onChange(id, intValue);
|
|
25
|
+
setOpen(false);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const handleClose = () => {
|
|
29
|
+
setIntValue(value);
|
|
30
|
+
setOpen(false);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const handleChangeField = (val) => {
|
|
34
|
+
setIntValue(val);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const TableauNotDisplayed = () => {
|
|
38
|
+
return (
|
|
39
|
+
<div className="tableau-block not_displayed_tableau">
|
|
40
|
+
<div className="tableau-info">
|
|
41
|
+
{intValue && intValue.general && !intValue.general.url ? (
|
|
42
|
+
<p className="tableau-error">URL required</p>
|
|
43
|
+
) : tableauError ? (
|
|
44
|
+
<p className="tableau-error">{tableauError}</p>
|
|
45
|
+
) : (
|
|
46
|
+
''
|
|
47
|
+
)}
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
let schema = Schema(config);
|
|
54
|
+
|
|
55
|
+
React.useEffect(() => {
|
|
56
|
+
if (!intValue?.options) {
|
|
57
|
+
setIntValue({
|
|
58
|
+
...intValue,
|
|
59
|
+
options: {
|
|
60
|
+
autoScale: false,
|
|
61
|
+
hideTabs: false,
|
|
62
|
+
hideToolbar: false,
|
|
63
|
+
toolbarPosition: 'Top',
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
68
|
+
}, [intValue]);
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<FormFieldWrapper {...props}>
|
|
72
|
+
<div className="wrapper">
|
|
73
|
+
<Button
|
|
74
|
+
floated="right"
|
|
75
|
+
size="tiny"
|
|
76
|
+
onClick={(e) => {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
e.stopPropagation();
|
|
79
|
+
setOpen(true);
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
Open Tableau Editor
|
|
83
|
+
</Button>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
{open && (
|
|
87
|
+
<Modal
|
|
88
|
+
id="tableau-editor-modal"
|
|
89
|
+
style={{ width: '95% !important' }}
|
|
90
|
+
open={true}
|
|
91
|
+
>
|
|
92
|
+
<Modal.Content scrolling>
|
|
93
|
+
<Grid stackable reversed="mobile vertically tablet vertically">
|
|
94
|
+
<Grid.Column
|
|
95
|
+
mobile={12}
|
|
96
|
+
tablet={12}
|
|
97
|
+
computer={5}
|
|
98
|
+
className="tableau-editor-column"
|
|
99
|
+
>
|
|
100
|
+
<InlineForm
|
|
101
|
+
block={block}
|
|
102
|
+
schema={schema}
|
|
103
|
+
onChangeField={(id, value) => {
|
|
104
|
+
handleChangeField(value);
|
|
105
|
+
}}
|
|
106
|
+
formData={dataForm}
|
|
107
|
+
/>
|
|
108
|
+
</Grid.Column>
|
|
109
|
+
<Grid.Column mobile={12} tablet={12} computer={7}>
|
|
110
|
+
{(intValue && intValue.general && !intValue.general.url) ||
|
|
111
|
+
tableauError ? (
|
|
112
|
+
<TableauNotDisplayed />
|
|
113
|
+
) : (
|
|
114
|
+
''
|
|
115
|
+
)}
|
|
116
|
+
<div className="tableau-container">
|
|
117
|
+
<TableauView
|
|
118
|
+
setTableauError={setTableauError}
|
|
119
|
+
data={{
|
|
120
|
+
...intValue?.general,
|
|
121
|
+
...intValue?.options,
|
|
122
|
+
...intValue?.extraOptions,
|
|
123
|
+
}}
|
|
124
|
+
/>
|
|
125
|
+
</div>
|
|
126
|
+
</Grid.Column>
|
|
127
|
+
</Grid>
|
|
128
|
+
</Modal.Content>
|
|
129
|
+
<Modal.Actions>
|
|
130
|
+
<Grid>
|
|
131
|
+
<Grid.Row>
|
|
132
|
+
<div className="map-edit-actions-container">
|
|
133
|
+
<Button onClick={handleClose}>Close</Button>
|
|
134
|
+
<Button color="green" onClick={handleApplyChanges}>
|
|
135
|
+
Apply changes
|
|
136
|
+
</Button>
|
|
137
|
+
</div>
|
|
138
|
+
</Grid.Row>
|
|
139
|
+
</Grid>
|
|
140
|
+
</Modal.Actions>
|
|
141
|
+
</Modal>
|
|
142
|
+
)}
|
|
143
|
+
{(intValue && intValue.general && !intValue.general.url) ||
|
|
144
|
+
tableauError ? (
|
|
145
|
+
<TableauNotDisplayed />
|
|
146
|
+
) : (
|
|
147
|
+
''
|
|
148
|
+
)}
|
|
149
|
+
|
|
150
|
+
<TableauView
|
|
151
|
+
setTableauError={setTableauError}
|
|
152
|
+
data={{ ...value?.general, ...value?.options, ...value?.extraOptions }}
|
|
153
|
+
/>
|
|
154
|
+
</FormFieldWrapper>
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export default VisualizationWidget;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
const generalSchema = {
|
|
2
|
+
title: 'General',
|
|
3
|
+
|
|
4
|
+
fieldsets: [
|
|
5
|
+
{
|
|
6
|
+
id: 'general',
|
|
7
|
+
title: 'General',
|
|
8
|
+
fields: ['url'],
|
|
9
|
+
},
|
|
10
|
+
],
|
|
11
|
+
|
|
12
|
+
properties: {
|
|
13
|
+
url: {
|
|
14
|
+
title: 'Url',
|
|
15
|
+
type: 'textarea',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
required: ['url'],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const optionsSchema = {
|
|
22
|
+
title: 'Options',
|
|
23
|
+
|
|
24
|
+
fieldsets: [
|
|
25
|
+
{
|
|
26
|
+
id: 'options',
|
|
27
|
+
title: 'Options',
|
|
28
|
+
fields: [
|
|
29
|
+
'sheetname',
|
|
30
|
+
'hideTabs',
|
|
31
|
+
'hideToolbar',
|
|
32
|
+
'autoScale',
|
|
33
|
+
'toolbarPosition',
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
|
|
38
|
+
properties: {
|
|
39
|
+
sheetname: {
|
|
40
|
+
title: 'Sheetname',
|
|
41
|
+
type: 'text',
|
|
42
|
+
},
|
|
43
|
+
hideTabs: {
|
|
44
|
+
title: 'Hide tabs',
|
|
45
|
+
type: 'boolean',
|
|
46
|
+
default: false,
|
|
47
|
+
},
|
|
48
|
+
hideToolbar: {
|
|
49
|
+
title: 'Hide toolbar',
|
|
50
|
+
type: 'boolean',
|
|
51
|
+
default: false,
|
|
52
|
+
},
|
|
53
|
+
autoScale: {
|
|
54
|
+
title: 'Auto scale',
|
|
55
|
+
type: 'boolean',
|
|
56
|
+
default: false,
|
|
57
|
+
description: 'Scale down tableau according to width',
|
|
58
|
+
},
|
|
59
|
+
toolbarPosition: {
|
|
60
|
+
title: 'Toolbar position',
|
|
61
|
+
type: 'array',
|
|
62
|
+
choices: [
|
|
63
|
+
['Top', 'Top'],
|
|
64
|
+
['Bottom', 'Bottom'],
|
|
65
|
+
],
|
|
66
|
+
default: 'Top',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
required: [],
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const urlParametersSchema = {
|
|
73
|
+
title: 'Parameter',
|
|
74
|
+
fieldsets: [
|
|
75
|
+
{ id: 'default', title: 'Default', fields: ['field', 'urlParam'] },
|
|
76
|
+
],
|
|
77
|
+
properties: {
|
|
78
|
+
field: {
|
|
79
|
+
title: 'Tableau fieldname',
|
|
80
|
+
type: 'text',
|
|
81
|
+
},
|
|
82
|
+
urlParam: {
|
|
83
|
+
title: 'URL param',
|
|
84
|
+
type: 'text',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
required: [],
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const breakpointUrlSchema = (config) => {
|
|
91
|
+
const breakpoints = config.blocks.blocksConfig.tableau_block.breakpoints;
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
title: 'URL',
|
|
95
|
+
fieldsets: [{ id: 'default', title: 'Default', fields: ['device', 'url'] }],
|
|
96
|
+
properties: {
|
|
97
|
+
device: {
|
|
98
|
+
title: 'Device',
|
|
99
|
+
type: 'array',
|
|
100
|
+
choices: Object.keys(breakpoints).map((breakpoint) => [
|
|
101
|
+
breakpoint,
|
|
102
|
+
breakpoint,
|
|
103
|
+
]),
|
|
104
|
+
},
|
|
105
|
+
url: {
|
|
106
|
+
title: 'Url',
|
|
107
|
+
widget: 'textarea',
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
required: [],
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const extraOptionsSchema = (config) => {
|
|
115
|
+
return {
|
|
116
|
+
title: 'Extra Options',
|
|
117
|
+
|
|
118
|
+
fieldsets: [
|
|
119
|
+
{
|
|
120
|
+
id: 'default',
|
|
121
|
+
title: 'Extra Options Data',
|
|
122
|
+
fields: ['urlParameters', 'breakpointUrls'],
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
|
|
126
|
+
properties: {
|
|
127
|
+
urlParameters: {
|
|
128
|
+
title: 'URL parameters',
|
|
129
|
+
widget: 'object_list',
|
|
130
|
+
schema: urlParametersSchema,
|
|
131
|
+
description: 'Set a list of url parameters to filter the tableau',
|
|
132
|
+
},
|
|
133
|
+
breakpointUrls: {
|
|
134
|
+
title: 'Breakpoint urls',
|
|
135
|
+
widget: 'object_list',
|
|
136
|
+
schema: breakpointUrlSchema(config),
|
|
137
|
+
description: 'Set different vizualization for specific breakpoint',
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
required: [],
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export default (config) => {
|
|
145
|
+
return {
|
|
146
|
+
title: 'Tableau Editor',
|
|
147
|
+
fieldsets: [
|
|
148
|
+
{
|
|
149
|
+
id: 'default',
|
|
150
|
+
title: 'Tableau Editor Sections',
|
|
151
|
+
fields: ['tableau_data'],
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
properties: {
|
|
155
|
+
tableau_data: {
|
|
156
|
+
title: 'Panels',
|
|
157
|
+
widget: 'object_types_widget',
|
|
158
|
+
schemas: [
|
|
159
|
+
{
|
|
160
|
+
id: 'general',
|
|
161
|
+
schema: generalSchema,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: 'options',
|
|
165
|
+
schema: optionsSchema,
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: 'extraOptions',
|
|
169
|
+
schema: extraOptionsSchema(config),
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
required: [],
|
|
175
|
+
};
|
|
176
|
+
};
|
package/src/helpers.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const loadTableauScript = (callback, version) => {
|
|
2
|
-
const existingScript = document.getElementById(`tableauJS`);
|
|
2
|
+
const existingScript = __CLIENT__ && document.getElementById(`tableauJS`);
|
|
3
3
|
//replace script loaded on each version change
|
|
4
4
|
if (existingScript) {
|
|
5
5
|
existingScript.setAttribute(
|
|
@@ -7,7 +7,7 @@ const loadTableauScript = (callback, version) => {
|
|
|
7
7
|
`https://public.tableau.com/javascripts/api/tableau-${version}.min.js`,
|
|
8
8
|
);
|
|
9
9
|
}
|
|
10
|
-
if (!existingScript) {
|
|
10
|
+
if (!existingScript && __CLIENT__) {
|
|
11
11
|
const script = document.createElement('script');
|
|
12
12
|
script.src = `https://public.tableau.com/javascripts/api/tableau-${version}.min.js`;
|
|
13
13
|
script.id = `tableauJS`;
|
|
@@ -18,14 +18,21 @@ const loadTableauScript = (callback, version) => {
|
|
|
18
18
|
}
|
|
19
19
|
//callback, if needed
|
|
20
20
|
if (existingScript && callback) callback();
|
|
21
|
+
|
|
22
|
+
const tableau = isMyScriptLoaded(version) && __CLIENT__ ? window.tableau : '';
|
|
23
|
+
return tableau;
|
|
21
24
|
};
|
|
22
25
|
|
|
23
|
-
const isMyScriptLoaded = (
|
|
26
|
+
const isMyScriptLoaded = (version) => {
|
|
24
27
|
//check for loaded Tableau script in dom scripts
|
|
25
28
|
var scripts = document.getElementsByTagName('script');
|
|
26
29
|
for (var i = scripts.length; i--; ) {
|
|
27
30
|
// eslint-disable-next-line eqeqeq
|
|
28
|
-
if (
|
|
31
|
+
if (
|
|
32
|
+
scripts[i].src ===
|
|
33
|
+
`https://public.tableau.com/javascripts/api/tableau-${version}.min.js`
|
|
34
|
+
)
|
|
35
|
+
return true;
|
|
29
36
|
}
|
|
30
37
|
return false;
|
|
31
38
|
};
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import sliderSVG from '@plone/volto/icons/slider.svg';
|
|
2
2
|
import TableauEdit from './TableauBlock/Edit';
|
|
3
3
|
import TableauView from './TableauBlock/View';
|
|
4
|
+
import EmbedTableauView from './Blocks/EmbedEEATableauBlock/View';
|
|
5
|
+
import EmbedTableauEdit from './Blocks/EmbedEEATableauBlock/Edit';
|
|
6
|
+
import { VisualizationView } from './Views';
|
|
7
|
+
import { VisualizationWidget } from './Widgets';
|
|
4
8
|
|
|
5
9
|
import tableauStore from './store';
|
|
6
10
|
|
|
@@ -44,6 +48,31 @@ const applyConfig = (config) => {
|
|
|
44
48
|
},
|
|
45
49
|
};
|
|
46
50
|
|
|
51
|
+
config.blocks.blocksConfig.embed_eea_tableau_block = {
|
|
52
|
+
id: 'embed_eea_tableau_block',
|
|
53
|
+
title: 'Embed EEA Tableau',
|
|
54
|
+
icon: sliderSVG,
|
|
55
|
+
group: 'common',
|
|
56
|
+
edit: EmbedTableauEdit,
|
|
57
|
+
view: EmbedTableauView,
|
|
58
|
+
restricted: false,
|
|
59
|
+
mostUsed: false,
|
|
60
|
+
sidebarTab: 1,
|
|
61
|
+
blocks: {},
|
|
62
|
+
security: {
|
|
63
|
+
addPermission: [],
|
|
64
|
+
view: [],
|
|
65
|
+
},
|
|
66
|
+
breakpoints: {
|
|
67
|
+
desktop: [Infinity, 982],
|
|
68
|
+
tablet: [981, 768],
|
|
69
|
+
mobile: [767, 0],
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
config.views.contentTypesViews.tableau_visualization = VisualizationView;
|
|
74
|
+
config.widgets.id.tableau_visualization_data = VisualizationWidget;
|
|
75
|
+
|
|
47
76
|
return config;
|
|
48
77
|
};
|
|
49
78
|
|