@eeacms/volto-tableau 3.0.3 → 3.0.5

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,6 +4,22 @@ 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
+ ### [3.0.5](https://github.com/eea/volto-tableau/compare/3.0.4...3.0.5) - 26 January 2023
8
+
9
+ #### :hammer_and_wrench: Others
10
+
11
+ - Add download options [dana-cfc4 - [`47031b2`](https://github.com/eea/volto-tableau/commit/47031b277cbde58a626076eef9d4fdb42c9400e2)]
12
+ - Remove version chooser [dana-cfc4 - [`34ceaf9`](https://github.com/eea/volto-tableau/commit/34ceaf9a89ea0fcc5b001cc86fd3af709629693a)]
13
+ - Remove height setting, solve no source link bug [dana-cfc4 - [`03216e3`](https://github.com/eea/volto-tableau/commit/03216e3d159ef9cf1b71702a3a409ab41f3cd427)]
14
+ - links to deps [Andrei Grigore - [`4cec1b2`](https://github.com/eea/volto-tableau/commit/4cec1b283a2cad800dbc9081caee2a03847060cd)]
15
+ - Update README.md [dana-cfc4 - [`83e4e7a`](https://github.com/eea/volto-tableau/commit/83e4e7a94892caa42cb5bcc4cb02b923dca87379)]
16
+ - Add privacy protection fields [dana-cfc4 - [`78cb29f`](https://github.com/eea/volto-tableau/commit/78cb29f29859aed7deaf18cb678bd8e689643883)]
17
+ ### [3.0.4](https://github.com/eea/volto-tableau/compare/3.0.3...3.0.4) - 18 January 2023
18
+
19
+ #### :hammer_and_wrench: Others
20
+
21
+ - Fix lint issues [dana-cfc4 - [`9bbf908`](https://github.com/eea/volto-tableau/commit/9bbf908195a9e90c606ebc87042ae3c7a44e7973)]
22
+ - Add available fields, remove default version, manage no version selected [dana-cfc4 - [`66e510e`](https://github.com/eea/volto-tableau/commit/66e510e5f2e47e049e157ab046d0499f14b8f700)]
7
23
  ### [3.0.3](https://github.com/eea/volto-tableau/compare/3.0.2...3.0.3) - 17 January 2023
8
24
 
9
25
  #### :hammer_and_wrench: Others
package/README.md CHANGED
@@ -16,6 +16,10 @@
16
16
 
17
17
  [Volto](https://github.com/plone/volto) add-on
18
18
 
19
+ ## Tableau
20
+
21
+ Registers a VisualizationView component for a content type named 'tableau_visualization'.
22
+
19
23
  ## Features
20
24
 
21
25
  Demo GIF
@@ -155,6 +159,23 @@ Generic command, does not automatically add the `beta` to version, but you can s
155
159
 
156
160
  > Do not keep Pull Requests from develop to master branches open when you are doing beta releases from the develop branch. As long as a PR to master is open, an automatic script will run on every commit and will update both the version and the changelog to a production-ready state - ( MAJOR.MINOR.PATCH mandatory format for version).
157
161
 
162
+ ## Enable Sources
163
+
164
+ https://github.com/eea/eea.coremetadata EEA Core Metadata addon is needed.
165
+
166
+ Sources (Data provenance) should be set on the visualization. To enable this, "EEA Core Metadata" should be checked as behavior on the visualization content-type.
167
+
168
+ controlpanel/dexterity-types/tableau_visualization
169
+
170
+ After this, sources can be added from the visualization edit interface. "EEA core metadata" tab => "Add source"
171
+
172
+
173
+ ## Add a Tableau Visualization
174
+
175
+ 1. Add the corresponding content-type. Project should also contain https://github.com/eea/eea.api.dataconnector which hosts the content-type.
176
+ 2. Fill in the mandatory fields
177
+ 3. "Tableau Widget" section => "Open Tableau Editor"
178
+ 4. Provide the necessary settings to create the visualization
158
179
 
159
180
  ## How to contribute
160
181
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-tableau",
3
- "version": "3.0.3",
3
+ "version": "3.0.5",
4
4
  "description": "@eeacms/volto-tableau: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -36,35 +36,28 @@ const View = (props) => {
36
36
  <PrivacyProtection data={data} {...props}>
37
37
  {data?.vis_url ? (
38
38
  <>
39
- <div className="tableau-block">
40
- {props.mode === 'edit' ? (
41
- <div className="tableau-info">
42
- <h3 className="tableau-version">
43
- == Tableau{' '}
44
- {tableau_visualization?.general?.version || '2.8.0'} loaded
45
- ==
46
- </h3>
47
- </div>
48
- ) : (
49
- ''
50
- )}
51
- <ConnectedTableau
52
- {...props.tableau_visualization}
53
- id={props.id}
54
- {...props}
55
- />
56
- </div>
39
+ {tableau_visualization?.general?.url ? (
40
+ <>
41
+ <ConnectedTableau
42
+ {...props.tableau_visualization}
43
+ id={props.id}
44
+ {...props}
45
+ />
57
46
 
58
- {show_sources &&
59
- data_provenance &&
60
- data_provenance?.data?.data_provenance &&
61
- tableau_visualization ? (
62
- <Sources sources={data_provenance.data.data_provenance} />
63
- ) : show_sources ? (
64
- <div>Data provenance is not set in the visualization</div>
65
- ) : (
66
- ''
67
- )}
47
+ {show_sources &&
48
+ data_provenance &&
49
+ data_provenance?.data?.data_provenance &&
50
+ tableau_visualization ? (
51
+ <Sources sources={data_provenance.data.data_provenance} />
52
+ ) : show_sources ? (
53
+ <div>Data provenance is not set in the visualization</div>
54
+ ) : (
55
+ ''
56
+ )}
57
+ </>
58
+ ) : !tableau_visualization?.general?.url ? (
59
+ <div>Url is not set in the visualization</div>
60
+ ) : null}
68
61
  </>
69
62
  ) : (
70
63
  <div>Please select a visualization from block editor.</div>
@@ -1,3 +1,45 @@
1
+ const ProtectionSchema = () => ({
2
+ title: 'Data Protection',
3
+
4
+ fieldsets: [
5
+ {
6
+ id: 'default',
7
+ title: 'Default',
8
+ fields: [
9
+ 'privacy_statement',
10
+ 'privacy_cookie_key',
11
+ 'enabled',
12
+ 'background_image',
13
+ ],
14
+ },
15
+ ],
16
+
17
+ properties: {
18
+ privacy_statement: {
19
+ title: 'Privacy statement',
20
+ description: 'Defined in template. Change only if necessary',
21
+ widget: 'slate_richtext',
22
+ className: 'slate-Widget',
23
+ },
24
+ privacy_cookie_key: {
25
+ title: 'Privacy cookie key',
26
+ description: 'Use default for Tableau, otherwise change',
27
+ defaultValue: 'tableau',
28
+ },
29
+ enabled: {
30
+ title: 'Data protection disclaimer enabled',
31
+ description: 'Enable/disable the privacy protection',
32
+ type: 'boolean',
33
+ },
34
+ background_image: {
35
+ title: 'Tableau preview image',
36
+ widget: 'file',
37
+ },
38
+ },
39
+
40
+ required: [],
41
+ });
42
+
1
43
  const Schema = (props) => {
2
44
  return {
3
45
  title: 'Embed EEA Tableau',
@@ -5,7 +47,7 @@ const Schema = (props) => {
5
47
  {
6
48
  id: 'default',
7
49
  title: 'Default',
8
- fields: ['vis_url', 'height', 'show_sources'],
50
+ fields: ['vis_url', 'show_sources', 'dataprotection'],
9
51
  },
10
52
  ],
11
53
  properties: {
@@ -13,15 +55,14 @@ const Schema = (props) => {
13
55
  widget: 'object_by_path',
14
56
  title: 'Visualization',
15
57
  },
16
- height: {
17
- title: 'Height',
18
- type: 'number',
19
- default: 450,
20
- },
21
58
  show_sources: {
22
59
  title: 'Toggle sources',
23
60
  type: 'boolean',
24
61
  },
62
+ dataprotection: {
63
+ widget: 'object',
64
+ schema: ProtectionSchema(),
65
+ },
25
66
  },
26
67
 
27
68
  required: ['vis_url'],
@@ -6,6 +6,13 @@ const ConnectedTableau = (props) => {
6
6
  const [loaded, setLoaded] = React.useState(null);
7
7
  return (
8
8
  <div className="tableau-block">
9
+ {loaded && props.mode === 'edit' ? (
10
+ <div className="tableau-info">
11
+ <h3 className="tableau-version">== Tableau ==</h3>
12
+ </div>
13
+ ) : (
14
+ ''
15
+ )}
9
16
  <Tableau
10
17
  error={error}
11
18
  loaded={loaded}
@@ -13,7 +20,6 @@ const ConnectedTableau = (props) => {
13
20
  setLoaded={setLoaded}
14
21
  data={{ ...props?.general, ...props?.options, ...props?.extraOptions }}
15
22
  url={props?.general?.url}
16
- version={props?.general?.version}
17
23
  {...props}
18
24
  />
19
25
  </div>
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import '@eeacms/volto-tableau/less/tableau.less';
3
+
4
+ const UrlParamsWidget = () => {
5
+ const fields = [
6
+ 'embed',
7
+ 'isGuestRedirectFromVizportal',
8
+ 'showShareOptions',
9
+ 'jsdebug',
10
+ 'sheetname',
11
+ 'display_count',
12
+ 'showVizHome',
13
+ 'origin',
14
+ 'device',
15
+ ];
16
+
17
+ return (
18
+ <div className="availableFieldsContainer">
19
+ <p className="availableFieldsTitle">Available Fields:</p>
20
+ {fields.map((field) => (
21
+ <p className="availableFields">
22
+ <strong>{field}</strong>
23
+ </p>
24
+ ))}
25
+ </div>
26
+ );
27
+ };
28
+
29
+ export default UrlParamsWidget;
@@ -0,0 +1,62 @@
1
+ import React from 'react';
2
+ import { Popup, Button, Modal } from 'semantic-ui-react';
3
+ import { Icon } from '@plone/volto/components';
4
+ import downloadSVG from '@plone/volto/icons/download.svg';
5
+
6
+ const TableauDownload = (props) => {
7
+ const [open, setOpen] = React.useState(false);
8
+ const viz = props.viz || {};
9
+
10
+ const exportImage = () => {
11
+ viz.showExportImageDialog();
12
+ };
13
+
14
+ const exportToCSV = () => {
15
+ viz.showExportCrossTabDialog();
16
+ };
17
+
18
+ const exportToExcel = () => {
19
+ viz.exportCrossTabToExcel();
20
+ };
21
+
22
+ return (
23
+ <>
24
+ <Popup
25
+ basic
26
+ className="tableau-download-dialog"
27
+ position="top center"
28
+ on="click"
29
+ trigger={
30
+ <div className="toolbar-button-wrapper">
31
+ <Button className="toolbar-button" title="Download">
32
+ <Icon name={downloadSVG} size="26px" />
33
+ </Button>
34
+ <span className="btn-text">Save</span>
35
+ </div>
36
+ }
37
+ >
38
+ <Popup.Header>Download</Popup.Header>
39
+ <Popup.Content>
40
+ <p>Select your file format.</p>
41
+ <Button onClick={exportImage}>Image</Button>
42
+ <Button onClick={exportToCSV}>CSV</Button>
43
+ <Button onClick={exportToExcel}>Excel</Button>
44
+ </Popup.Content>
45
+ </Popup>
46
+
47
+ <Modal onClose={() => setOpen(false)} open={open}>
48
+ <Modal.Content>
49
+ Permissions are required to download the workbook.
50
+ </Modal.Content>
51
+
52
+ <Modal.Actions>
53
+ <Button primary onClick={() => setOpen(false)}>
54
+ OK
55
+ </Button>
56
+ </Modal.Actions>
57
+ </Modal>
58
+ </>
59
+ );
60
+ };
61
+
62
+ export default TableauDownload;
@@ -0,0 +1,78 @@
1
+ import React from 'react';
2
+ import { Modal, Button } from 'semantic-ui-react';
3
+ import { Icon } from '@plone/volto/components';
4
+ import { useHistory, useLocation } from 'react-router-dom';
5
+ import fullscreenSVG from '@plone/volto/icons/fullscreen.svg';
6
+
7
+ import config from '@plone/volto/registry';
8
+
9
+ const TableauFullscreen = (props) => {
10
+ const tableau_url = props.data.url;
11
+ const modalHash = props?.item.getId + '_preview';
12
+ const [open, setOpen] = React.useState(false);
13
+ const history = useHistory();
14
+ const location = useLocation();
15
+ const {
16
+ blocks: { blocksConfig },
17
+ } = config;
18
+ const TableauBlockView = blocksConfig.tableau_block.view;
19
+
20
+ React.useEffect(() => {
21
+ if (location.hash.includes(modalHash)) {
22
+ setOpen(true);
23
+ } else {
24
+ setOpen(false);
25
+ }
26
+ }, [location, modalHash]);
27
+
28
+ const closeModal = () => {
29
+ history.push({
30
+ hash: '',
31
+ });
32
+ setOpen(false);
33
+ };
34
+
35
+ return (
36
+ <>
37
+ <div className="toolbar-button-wrapper">
38
+ <Button
39
+ className="toolbar-button"
40
+ title="Full Screen"
41
+ onClick={() => {
42
+ setOpen(true);
43
+ if (props.item) {
44
+ history.push({
45
+ hash: props.item.getId + '_preview',
46
+ });
47
+ }
48
+ }}
49
+ >
50
+ <Icon name={fullscreenSVG} size="23px" />
51
+ </Button>
52
+ <span className="btn-text">Enlarge</span>
53
+ </div>
54
+
55
+ <Modal
56
+ className="tableau-fullscreen"
57
+ onClose={closeModal}
58
+ onOpen={() => setOpen(true)}
59
+ open={open}
60
+ >
61
+ <Modal.Content>
62
+ <TableauBlockView
63
+ {...props}
64
+ data={{ url: tableau_url, hideToolbar: true }}
65
+ ></TableauBlockView>
66
+ </Modal.Content>
67
+
68
+ <Modal.Actions>
69
+ <Button primary onClick={closeModal}>
70
+ Close
71
+ </Button>
72
+ </Modal.Actions>
73
+ </Modal>
74
+ </>
75
+ );
76
+ };
77
+
78
+ export default TableauFullscreen;
@@ -0,0 +1,81 @@
1
+ import React from 'react';
2
+ import { Popup, Tab, Button, Menu, Input } from 'semantic-ui-react';
3
+ import { Icon } from '@plone/volto/components';
4
+ import useCopyToClipboard from '../downloadHelpers/downloadHelpers';
5
+
6
+ import shareSVG from '@plone/volto/icons/share.svg';
7
+ import linkSVG from '@plone/volto/icons/link.svg';
8
+
9
+ import cx from 'classnames';
10
+
11
+ const TableauShare = (props) => {
12
+ const tableau_url = props.data.url;
13
+
14
+ const CopyUrlButton = ({ url, buttonText }) => {
15
+ const [copyUrlStatus, copyUrl] = useCopyToClipboard(url);
16
+
17
+ if (copyUrlStatus === 'copied') {
18
+ buttonText = 'Copied!';
19
+ } else if (copyUrlStatus === 'failed') {
20
+ buttonText = 'Copy failed. Please try again.';
21
+ }
22
+
23
+ return (
24
+ <Button
25
+ primary
26
+ onClick={copyUrl}
27
+ className={cx('copy-button', {
28
+ 'green-button': copyUrlStatus === 'copied',
29
+ })}
30
+ >
31
+ {buttonText}
32
+ </Button>
33
+ );
34
+ };
35
+
36
+ const panes = [
37
+ {
38
+ menuItem: (
39
+ <Menu.Item key="location">
40
+ <span className="nav-dot">
41
+ <Icon name={linkSVG} size="24px" />
42
+ </span>
43
+ <span className="nav-dot-title">URL</span>
44
+ </Menu.Item>
45
+ ),
46
+ render: () => (
47
+ <Tab.Pane>
48
+ <Input defaultValue={tableau_url} />
49
+ <CopyUrlButton url={tableau_url} buttonText="Copy sharing URL" />
50
+ </Tab.Pane>
51
+ ),
52
+ },
53
+ ];
54
+
55
+ return (
56
+ <Popup
57
+ basic
58
+ className="tableau-share-dialog"
59
+ position="top center"
60
+ on="click"
61
+ trigger={
62
+ <div className="toolbar-button-wrapper">
63
+ <Button className="toolbar-button" title="Share">
64
+ <Icon name={shareSVG} size="26px" />
65
+ </Button>
66
+ <span className="btn-text">Share</span>
67
+ </div>
68
+ }
69
+ >
70
+ <Popup.Header>Share Visualization</Popup.Header>
71
+ <Popup.Content>
72
+ <Tab
73
+ menu={{ secondary: true, pointing: true, fluid: true }}
74
+ panes={panes}
75
+ />
76
+ </Popup.Content>
77
+ </Popup>
78
+ );
79
+ };
80
+
81
+ export default TableauShare;
@@ -0,0 +1,152 @@
1
+ .dashboard-wrapper {
2
+ display: flex;
3
+ flex-direction: row;
4
+ flex-wrap: wrap;
5
+ background-color: #f5f5f5;
6
+
7
+ .tableau-block {
8
+ position: relative;
9
+ display: flex;
10
+ flex: 1 1;
11
+ flex-direction: column;
12
+ padding: 1em 0.5em;
13
+ }
14
+
15
+ .toolbar-button {
16
+ position: relative;
17
+ width: 32px;
18
+ height: 32px;
19
+ padding: 2px !important;
20
+ background-color: #ff421b !important;
21
+ border-radius: 3px !important;
22
+ color: white !important;
23
+
24
+ &:hover {
25
+ opacity: 0.6;
26
+ }
27
+
28
+ svg {
29
+ position: absolute;
30
+ top: 50%;
31
+ left: 50%;
32
+ margin: 0 !important;
33
+ fill: #fff !important;
34
+ -webkit-transform: translate(-50%, -50%);
35
+ transform: translate(-50%, -50%);
36
+ }
37
+ }
38
+ }
39
+
40
+ .tableau-download-dialog {
41
+ max-width: 282px !important;
42
+
43
+ .header {
44
+ padding: 0.5em 0 !important;
45
+ }
46
+
47
+ button {
48
+ display: block;
49
+ width: 100%;
50
+ margin: 5px 0 !important;
51
+ }
52
+ }
53
+
54
+ .tableau-share-dialog {
55
+ min-width: 400px !important;
56
+
57
+ .ui.secondary.pointing.menu .item {
58
+ display: flex;
59
+ flex-direction: column;
60
+ padding: 10px 25px;
61
+ }
62
+
63
+ .nav-dot-title {
64
+ margin-top: 5px;
65
+ font-size: 14px;
66
+ }
67
+
68
+ .ui.secondary.pointing.menu .active.item {
69
+ border-color: #09b;
70
+
71
+ .nav-dot-title {
72
+ font-weight: 500;
73
+ }
74
+
75
+ .nav-dot {
76
+ opacity: 1;
77
+ }
78
+ }
79
+
80
+ .nav-dot {
81
+ display: flex;
82
+ width: 37px;
83
+ height: 37px;
84
+ align-items: center;
85
+ justify-content: center;
86
+ margin: 8px auto;
87
+ background-color: #09b;
88
+ border-radius: 50%;
89
+ opacity: 0.6;
90
+
91
+ svg {
92
+ fill: #fff !important;
93
+ }
94
+ }
95
+
96
+ .ui.secondary.pointing.menu .active.item:hover {
97
+ border-color: #09b;
98
+ }
99
+
100
+ .ui.tab {
101
+ .ui.input {
102
+ display: block;
103
+ margin-bottom: 1em;
104
+
105
+ input {
106
+ width: 100%;
107
+ }
108
+ }
109
+
110
+ textarea {
111
+ display: block;
112
+ width: 100%;
113
+ height: 100px;
114
+ padding: 6px 12px;
115
+ border: 1px solid grey;
116
+ margin-bottom: 1.5em;
117
+ color: #696969;
118
+ font-family: 'Lucida Console', Monaco, monospace;
119
+ font-size: 13px;
120
+ }
121
+ }
122
+ }
123
+
124
+ .copy-button {
125
+ margin-top: 1rem !important;
126
+ }
127
+
128
+ .copy-button.green-button {
129
+ background-color: #269b65 !important;
130
+ }
131
+
132
+ .toolbar-button-wrapper {
133
+ display: flex;
134
+ flex-direction: column;
135
+ text-align: center;
136
+
137
+ .btn-text {
138
+ font-family: Roboto, Helvetica Neue, Arial, Helvetica, sans-serif;
139
+ font-size: 12px;
140
+ font-weight: 400;
141
+ line-height: 20px;
142
+ }
143
+
144
+ .ui.button.toolbar-button {
145
+ margin: 0 7px !important;
146
+ }
147
+ }
148
+
149
+ .tableau-icons {
150
+ display: flex;
151
+ flex-direction: row;
152
+ }
@@ -28,17 +28,19 @@ const SourcesWidget = ({ sources }) => {
28
28
  <ul>
29
29
  {sources &&
30
30
  sources.data &&
31
- sources.data.map((param, i) => (
32
- <li key={i} className="embed-source-param">
33
- <UniversalLink
34
- className="embed-sources-param-title"
35
- href={param.link}
36
- >
37
- {param.title}
38
- </UniversalLink>
39
- , {param.organisation}
40
- </li>
41
- ))}
31
+ sources.data.map((param, i) =>
32
+ param.link ? (
33
+ <li key={i} className="embed-source-param">
34
+ <UniversalLink
35
+ className="embed-sources-param-title"
36
+ href={param.link}
37
+ >
38
+ {param.title}
39
+ </UniversalLink>
40
+ , {param.organisation}
41
+ </li>
42
+ ) : null,
43
+ )}
42
44
  </ul>
43
45
  )}
44
46
  </div>
@@ -6,6 +6,9 @@ import { Toast } from '@plone/volto/components';
6
6
  import { setTableauApi } from '@eeacms/volto-tableau/actions';
7
7
  import cx from 'classnames';
8
8
  import { loadTableauScript } from '../helpers';
9
+ import TableauDownload from '../DownloadExtras/TableauDownload';
10
+ import TableauShare from '../DownloadExtras/TableauShare';
11
+ import '../DownloadExtras/style.less';
9
12
 
10
13
  const Tableau = (props) => {
11
14
  const ref = React.useRef(null);
@@ -212,23 +215,29 @@ const Tableau = (props) => {
212
215
  ''
213
216
  ) : (
214
217
  <div className="tableau-loader">
215
- <span>
216
- {mode === 'edit'
217
- ? 'Loading...'
218
- : `Loading Tableau v${version}`}
219
- </span>
218
+ <span>Loading...</span>
220
219
  </div>
221
220
  )}
222
221
  </>
223
222
  ) : (
224
223
  <div>No data present in that visualization.</div>
225
224
  )}
226
- <div
227
- className={cx('tableau', version, {
228
- 'tableau-scale': autoScale,
229
- })}
230
- ref={ref}
231
- />
225
+ <div className="dashboard-wrapper">
226
+ <div className="tableau-block">
227
+ {viz ? (
228
+ <div className="tableau-icons">
229
+ <TableauDownload {...props} viz={viz} />
230
+ <TableauShare {...props} viz={viz} data={{ url: url }} />
231
+ </div>
232
+ ) : null}
233
+ <div
234
+ className={cx('tableau', version, {
235
+ 'tableau-scale': autoScale,
236
+ })}
237
+ ref={ref}
238
+ />
239
+ </div>
240
+ </div>
232
241
  </div>
233
242
  </div>
234
243
  );
@@ -34,8 +34,6 @@ const View = (props) => {
34
34
  description = null,
35
35
  autoScale = false,
36
36
  } = data;
37
- const version =
38
- props.data.version || config.settings.tableauVersion || '2.8.0';
39
37
  const device = getDevice(config, screen.page?.width || Infinity);
40
38
  const breakpointUrl = breakpointUrls.filter(
41
39
  (breakpoint) => breakpoint.device === device,
@@ -67,33 +65,33 @@ const View = (props) => {
67
65
 
68
66
  return mounted ? (
69
67
  <div className="tableau-block">
70
- {props.mode === 'edit' ? (
71
- <div className="tableau-info">
72
- <h3 className="tableau-version">== Tableau {version} loaded ==</h3>
73
- {!props.data.url ? <p className="tableau-error">URL required</p> : ''}
74
- {error ? <p className="tableau-error">{error}</p> : ''}
75
- </div>
76
- ) : (
77
- ''
78
- )}
68
+ <div className="tableau-info">
69
+ {loaded && url && props.mode === 'edit' ? (
70
+ <h3 className="tableau-version">== Tableau ==</h3>
71
+ ) : null}
72
+ {!url ? <p className="tableau-error">URL required</p> : ''}
73
+ {error ? <p className="tableau-error">{error}</p> : ''}
74
+ </div>
75
+
79
76
  {loaded && title ? <h3 className="tableau-title">{title}</h3> : ''}
80
77
  {loaded && description ? (
81
78
  <p className="tableau-description">{description}</p>
82
79
  ) : (
83
80
  ''
84
81
  )}
85
- <Tableau
86
- {...props}
87
- canUpdateUrl={!breakpointUrl}
88
- extraFilters={extraFilters}
89
- extraOptions={{ device: autoScale ? 'desktop' : device }}
90
- error={error}
91
- loaded={loaded}
92
- setError={setError}
93
- setLoaded={setLoaded}
94
- version={version}
95
- url={url}
96
- />
82
+ {url ? (
83
+ <Tableau
84
+ {...props}
85
+ canUpdateUrl={!breakpointUrl}
86
+ extraFilters={extraFilters}
87
+ extraOptions={{ device: autoScale ? 'desktop' : device }}
88
+ error={error}
89
+ loaded={loaded}
90
+ setError={setError}
91
+ setLoaded={setLoaded}
92
+ url={url}
93
+ />
94
+ ) : null}
97
95
  </div>
98
96
  ) : (
99
97
  ''
@@ -46,7 +46,7 @@ export default (config) => ({
46
46
  {
47
47
  id: 'default',
48
48
  title: 'Default',
49
- fields: ['version', 'url', 'title', 'description'],
49
+ fields: ['url', 'title', 'description'],
50
50
  },
51
51
  {
52
52
  id: 'options',
@@ -66,24 +66,6 @@ export default (config) => ({
66
66
  },
67
67
  ],
68
68
  properties: {
69
- version: {
70
- title: 'Version',
71
- type: 'array',
72
- choices: [
73
- ...[
74
- '2.8.0',
75
- '2.7.0',
76
- '2.6.0',
77
- '2.5.0',
78
- '2.4.0',
79
- '2.3.0',
80
- '2.2.2',
81
- '2.1.2',
82
- '2.0.3',
83
- ].map((version) => [version, `tableau-${version}`]),
84
- ],
85
- default: config.settings.tableauVersion || '2.8.0',
86
- },
87
69
  url: {
88
70
  title: 'Url',
89
71
  widget: 'textarea',
@@ -138,5 +120,5 @@ export default (config) => ({
138
120
  description: 'Set different vizualization for specific breakpoint',
139
121
  },
140
122
  },
141
- required: ['version', 'url'],
123
+ required: ['url'],
142
124
  });
@@ -23,19 +23,18 @@ const VisualizationView = (props) => {
23
23
  };
24
24
  return (
25
25
  <div>
26
- {!tableau_visualization_data.general?.url || tableauError ? (
26
+ {!tableau_visualization_data?.general?.url || tableauError ? (
27
27
  <TableauNotDisplayed />
28
28
  ) : (
29
- ''
29
+ <TableauView
30
+ setTableauError={setTableauError}
31
+ data={{
32
+ ...tableau_visualization_data.general,
33
+ ...tableau_visualization_data.options,
34
+ ...tableau_visualization_data.extraOptions,
35
+ }}
36
+ />
30
37
  )}
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
38
  </div>
40
39
  );
41
40
  };
@@ -53,14 +53,6 @@ const VisualizationWidget = (props) => {
53
53
  let schema = Schema(config);
54
54
 
55
55
  React.useEffect(() => {
56
- if (!intValue?.general || !intValue?.general?.version) {
57
- setIntValue({
58
- ...intValue,
59
- general: {
60
- version: '2.8.0',
61
- },
62
- });
63
- }
64
56
  if (!intValue?.options) {
65
57
  setIntValue({
66
58
  ...intValue,
@@ -73,7 +65,7 @@ const VisualizationWidget = (props) => {
73
65
  });
74
66
  }
75
67
  // eslint-disable-next-line react-hooks/exhaustive-deps
76
- }, [intValue]);
68
+ }, []);
77
69
 
78
70
  return (
79
71
  <FormFieldWrapper {...props}>
@@ -1,41 +1,21 @@
1
- const generalSchema = (config) => {
2
- return {
3
- title: 'General',
1
+ const generalSchema = {
2
+ title: 'General',
4
3
 
5
- fieldsets: [
6
- {
7
- id: 'general',
8
- title: 'General',
9
- fields: ['version', 'url'],
10
- },
11
- ],
4
+ fieldsets: [
5
+ {
6
+ id: 'general',
7
+ title: 'General',
8
+ fields: ['url'],
9
+ },
10
+ ],
12
11
 
13
- properties: {
14
- version: {
15
- title: 'Version',
16
- type: 'array',
17
- choices: [
18
- ...[
19
- '2.8.0',
20
- '2.7.0',
21
- '2.6.0',
22
- '2.5.0',
23
- '2.4.0',
24
- '2.3.0',
25
- '2.2.2',
26
- '2.1.2',
27
- '2.0.3',
28
- ].map((version) => [version, `tableau-${version}`]),
29
- ],
30
- default: config.settings.tableauVersion || '2.8.0',
31
- },
32
- url: {
33
- title: 'Url',
34
- type: 'textarea',
35
- },
12
+ properties: {
13
+ url: {
14
+ title: 'Url',
15
+ type: 'textarea',
36
16
  },
37
- required: ['url', 'version'],
38
- };
17
+ },
18
+ required: ['url'],
39
19
  };
40
20
 
41
21
  const optionsSchema = {
@@ -92,7 +72,11 @@ const optionsSchema = {
92
72
  const urlParametersSchema = {
93
73
  title: 'Parameter',
94
74
  fieldsets: [
95
- { id: 'default', title: 'Default', fields: ['field', 'urlParam'] },
75
+ {
76
+ id: 'default',
77
+ title: 'Default',
78
+ fields: ['field', 'urlParam'],
79
+ },
96
80
  ],
97
81
  properties: {
98
82
  field: {
@@ -139,7 +123,7 @@ const extraOptionsSchema = (config) => {
139
123
  {
140
124
  id: 'default',
141
125
  title: 'Extra Options Data',
142
- fields: ['urlParameters', 'breakpointUrls'],
126
+ fields: ['urlParameters', 'availableFields', 'breakpointUrls'],
143
127
  },
144
128
  ],
145
129
 
@@ -150,6 +134,10 @@ const extraOptionsSchema = (config) => {
150
134
  schema: urlParametersSchema,
151
135
  description: 'Set a list of url parameters to filter the tableau',
152
136
  },
137
+ availableFields: {
138
+ title: 'Available Fields:',
139
+ widget: 'url_params_widget',
140
+ },
153
141
  breakpointUrls: {
154
142
  title: 'Breakpoint urls',
155
143
  widget: 'object_list',
@@ -178,7 +166,7 @@ export default (config) => {
178
166
  schemas: [
179
167
  {
180
168
  id: 'general',
181
- schema: generalSchema(config),
169
+ schema: generalSchema,
182
170
  },
183
171
  {
184
172
  id: 'options',
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+
3
+ const useCopyToClipboard = (text) => {
4
+ const [copyStatus, setCopyStatus] = React.useState('inactive');
5
+ const copy = React.useCallback(() => {
6
+ navigator.clipboard.writeText(text).then(
7
+ () => setCopyStatus('copied'),
8
+ () => setCopyStatus('failed'),
9
+ );
10
+ }, [text]);
11
+
12
+ React.useEffect(() => {
13
+ if (copyStatus === 'inactive') {
14
+ return;
15
+ }
16
+
17
+ const timeout = setTimeout(() => setCopyStatus('inactive'), 3000);
18
+
19
+ return () => clearTimeout(timeout);
20
+ }, [copyStatus]);
21
+
22
+ return [copyStatus, copy];
23
+ };
24
+
25
+ export default useCopyToClipboard;
package/src/index.js CHANGED
@@ -5,6 +5,7 @@ import EmbedTableauView from './Blocks/EmbedEEATableauBlock/View';
5
5
  import EmbedTableauEdit from './Blocks/EmbedEEATableauBlock/Edit';
6
6
  import { VisualizationView } from './Views';
7
7
  import { VisualizationWidget } from './Widgets';
8
+ import UrlParamsWidget from './CustomWidgets/UrlParamsWidget';
8
9
 
9
10
  import tableauStore from './store';
10
11
 
@@ -72,6 +73,7 @@ const applyConfig = (config) => {
72
73
 
73
74
  config.views.contentTypesViews.tableau_visualization = VisualizationView;
74
75
  config.widgets.id.tableau_visualization_data = VisualizationWidget;
76
+ config.widgets.widget.url_params_widget = UrlParamsWidget;
75
77
 
76
78
  return config;
77
79
  };
@@ -108,4 +108,20 @@
108
108
  }
109
109
  }
110
110
 
111
- .loadAddonOverrides();
111
+ .availableFieldsContainer {
112
+ padding: 0 5px 5px 0;
113
+ border-bottom: 1px solid #d9d9d9;
114
+ }
115
+
116
+ .availableFieldsTitle {
117
+ color: darkgray;
118
+ font-size: 13px;
119
+ font-weight: bold;
120
+ }
121
+
122
+ .availableFields {
123
+ padding: 0 5px;
124
+ font-size: 12px;
125
+ }
126
+
127
+ .loadAddonVariables();