@eeacms/volto-tableau 3.0.4 → 3.0.6

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,18 @@ 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.6](https://github.com/eea/volto-tableau/compare/3.0.5...3.0.6) - 30 January 2023
8
+
9
+ ### [3.0.5](https://github.com/eea/volto-tableau/compare/3.0.4...3.0.5) - 26 January 2023
10
+
11
+ #### :hammer_and_wrench: Others
12
+
13
+ - Add download options [dana-cfc4 - [`47031b2`](https://github.com/eea/volto-tableau/commit/47031b277cbde58a626076eef9d4fdb42c9400e2)]
14
+ - Remove version chooser [dana-cfc4 - [`34ceaf9`](https://github.com/eea/volto-tableau/commit/34ceaf9a89ea0fcc5b001cc86fd3af709629693a)]
15
+ - Remove height setting, solve no source link bug [dana-cfc4 - [`03216e3`](https://github.com/eea/volto-tableau/commit/03216e3d159ef9cf1b71702a3a409ab41f3cd427)]
16
+ - links to deps [Andrei Grigore - [`4cec1b2`](https://github.com/eea/volto-tableau/commit/4cec1b283a2cad800dbc9081caee2a03847060cd)]
17
+ - Update README.md [dana-cfc4 - [`83e4e7a`](https://github.com/eea/volto-tableau/commit/83e4e7a94892caa42cb5bcc4cb02b923dca87379)]
18
+ - Add privacy protection fields [dana-cfc4 - [`78cb29f`](https://github.com/eea/volto-tableau/commit/78cb29f29859aed7deaf18cb678bd8e689643883)]
7
19
  ### [3.0.4](https://github.com/eea/volto-tableau/compare/3.0.3...3.0.4) - 18 January 2023
8
20
 
9
21
  #### :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.4",
3
+ "version": "3.0.6",
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,26 +36,14 @@ const View = (props) => {
36
36
  <PrivacyProtection data={data} {...props}>
37
37
  {data?.vis_url ? (
38
38
  <>
39
- {tableau_visualization?.general?.url &&
40
- tableau_visualization?.general?.version ? (
39
+ {tableau_visualization?.general?.url ? (
41
40
  <>
42
- <div className="tableau-block">
43
- {props.mode === 'edit' ? (
44
- <div className="tableau-info">
45
- <h3 className="tableau-version">
46
- == Tableau {tableau_visualization?.general?.version}{' '}
47
- loaded ==
48
- </h3>
49
- </div>
50
- ) : (
51
- ''
52
- )}
53
- <ConnectedTableau
54
- {...props.tableau_visualization}
55
- id={props.id}
56
- {...props}
57
- />
58
- </div>
41
+ <ConnectedTableau
42
+ {...props.tableau_visualization}
43
+ id={props.id}
44
+ {...props}
45
+ />
46
+
59
47
  {show_sources &&
60
48
  data_provenance &&
61
49
  data_provenance?.data?.data_provenance &&
@@ -69,9 +57,7 @@ const View = (props) => {
69
57
  </>
70
58
  ) : !tableau_visualization?.general?.url ? (
71
59
  <div>Url is not set in the visualization</div>
72
- ) : (
73
- <div>Version is not set in the visualization</div>
74
- )}
60
+ ) : null}
75
61
  </>
76
62
  ) : (
77
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,124 @@
1
+ import React from 'react';
2
+ import { Popup, Button, Modal, Dropdown } 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 viz = props.viz || {};
8
+
9
+ const [open, setOpen] = React.useState(false);
10
+ const [
11
+ isSheetSelectorCsvVisible,
12
+ setIsSheetSelectorCsvVisible,
13
+ ] = React.useState(false);
14
+ const [
15
+ isSheetSelectorExcelVisible,
16
+ setIsSheetSelectorExcelVisible,
17
+ ] = React.useState(false);
18
+ const [availableSheets, setAvailableSheets] = React.useState([]);
19
+
20
+ const initializeDropdown = () => {
21
+ const sheets = viz
22
+ ?.getWorkbook()
23
+ ?.getActiveSheet()
24
+ ?.getWorksheets()
25
+ .map((sheet, index) => ({
26
+ key: index,
27
+ text: sheet.getName(),
28
+ value: sheet.getName(),
29
+ }));
30
+ setAvailableSheets(sheets);
31
+ };
32
+
33
+ const exportImage = () => {
34
+ setIsSheetSelectorCsvVisible(false);
35
+ setIsSheetSelectorExcelVisible(false);
36
+ viz.showExportImageDialog();
37
+ };
38
+
39
+ const exportToCSV = () => {
40
+ setIsSheetSelectorExcelVisible(false);
41
+ initializeDropdown();
42
+ setIsSheetSelectorCsvVisible(true);
43
+ };
44
+
45
+ const exportToExcel = () => {
46
+ setIsSheetSelectorCsvVisible(false);
47
+ initializeDropdown();
48
+ setIsSheetSelectorExcelVisible(true);
49
+ };
50
+
51
+ const handleSheetSelectionCsvChange = (e, data) => {
52
+ viz.showExportCrossTabDialog(data.value);
53
+ };
54
+
55
+ const handleSheetSelectionExcelChange = (e, data) => {
56
+ viz.exportCrossTabToExcel(data.value);
57
+ };
58
+
59
+ const hideDropdowns = () => {
60
+ setIsSheetSelectorCsvVisible(false);
61
+ setIsSheetSelectorExcelVisible(false);
62
+ };
63
+
64
+ return (
65
+ <>
66
+ <Popup
67
+ basic
68
+ className="tableau-download-dialog"
69
+ position="top center"
70
+ on="click"
71
+ trigger={
72
+ <div className="toolbar-button-wrapper">
73
+ <Button
74
+ className="toolbar-button"
75
+ title="Download"
76
+ onClick={hideDropdowns}
77
+ >
78
+ <Icon name={downloadSVG} size="26px" />
79
+ </Button>
80
+ <span className="btn-text">Save</span>
81
+ </div>
82
+ }
83
+ >
84
+ <Popup.Header>Download</Popup.Header>
85
+ <Popup.Content>
86
+ <p>Select your file format.</p>
87
+ <Button onClick={exportImage}>Image</Button>
88
+ <Button onClick={exportToCSV}>CSV</Button>
89
+ {isSheetSelectorCsvVisible ? (
90
+ <Dropdown
91
+ placeholder="Sheet"
92
+ selection
93
+ options={availableSheets}
94
+ onChange={handleSheetSelectionCsvChange}
95
+ />
96
+ ) : null}
97
+ <Button onClick={exportToExcel}>Excel</Button>
98
+ {isSheetSelectorExcelVisible ? (
99
+ <Dropdown
100
+ placeholder="Sheet"
101
+ selection
102
+ options={availableSheets}
103
+ onChange={handleSheetSelectionExcelChange}
104
+ />
105
+ ) : null}
106
+ </Popup.Content>
107
+ </Popup>
108
+
109
+ <Modal onClose={() => setOpen(false)} open={open}>
110
+ <Modal.Content>
111
+ Permissions are required to download the workbook.
112
+ </Modal.Content>
113
+
114
+ <Modal.Actions>
115
+ <Button primary onClick={() => setOpen(false)}>
116
+ OK
117
+ </Button>
118
+ </Modal.Actions>
119
+ </Modal>
120
+ </>
121
+ );
122
+ };
123
+
124
+ 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);
@@ -23,7 +26,7 @@ const Tableau = (props) => {
23
26
  screen = {},
24
27
  setError = () => {},
25
28
  setLoaded = () => {},
26
- version = '',
29
+ version = '2.8.0',
27
30
  } = props;
28
31
  const {
29
32
  autoScale = false,
@@ -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,15 +34,12 @@ const View = (props) => {
34
34
  description = null,
35
35
  autoScale = false,
36
36
  } = data;
37
- const version = props.data.version || config.settings.tableauVersion;
38
37
  const device = getDevice(config, screen.page?.width || Infinity);
39
38
  const breakpointUrl = breakpointUrls.filter(
40
39
  (breakpoint) => breakpoint.device === device,
41
40
  )[0]?.url;
42
41
  const url = breakpointUrl || data.url;
43
42
 
44
- const displayCondition = url && version;
45
-
46
43
  React.useEffect(() => {
47
44
  setMounted(true);
48
45
  /* eslint-disable-next-line */
@@ -69,15 +66,10 @@ const View = (props) => {
69
66
  return mounted ? (
70
67
  <div className="tableau-block">
71
68
  <div className="tableau-info">
72
- {displayCondition && props.mode === 'edit' ? (
73
- <h3 className="tableau-version">== Tableau {version} loaded ==</h3>
69
+ {loaded && url && props.mode === 'edit' ? (
70
+ <h3 className="tableau-version">== Tableau ==</h3>
74
71
  ) : null}
75
72
  {!url ? <p className="tableau-error">URL required</p> : ''}
76
- {url && !version ? (
77
- <p className="tableau-error">Version required</p>
78
- ) : (
79
- ''
80
- )}
81
73
  {error ? <p className="tableau-error">{error}</p> : ''}
82
74
  </div>
83
75
 
@@ -87,7 +79,7 @@ const View = (props) => {
87
79
  ) : (
88
80
  ''
89
81
  )}
90
- {displayCondition ? (
82
+ {url ? (
91
83
  <Tableau
92
84
  {...props}
93
85
  canUpdateUrl={!breakpointUrl}
@@ -97,7 +89,6 @@ const View = (props) => {
97
89
  loaded={loaded}
98
90
  setError={setError}
99
91
  setLoaded={setLoaded}
100
- version={version}
101
92
  url={url}
102
93
  />
103
94
  ) : null}
@@ -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,
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
  });
@@ -12,8 +12,6 @@ const VisualizationView = (props) => {
12
12
  <div className="tableau-info">
13
13
  {!tableau_visualization_data.general?.url ? (
14
14
  <p className="tableau-error">URL required</p>
15
- ) : !tableau_visualization_data.general?.version ? (
16
- <p className="tableau-error">Version required</p>
17
15
  ) : tableauError ? (
18
16
  <p className="tableau-error">{tableauError}</p>
19
17
  ) : (
@@ -25,9 +23,7 @@ const VisualizationView = (props) => {
25
23
  };
26
24
  return (
27
25
  <div>
28
- {!tableau_visualization_data?.general?.url ||
29
- !tableau_visualization_data?.general?.version ||
30
- tableauError ? (
26
+ {!tableau_visualization_data?.general?.url || tableauError ? (
31
27
  <TableauNotDisplayed />
32
28
  ) : (
33
29
  <TableauView
@@ -40,8 +40,6 @@ const VisualizationWidget = (props) => {
40
40
  <div className="tableau-info">
41
41
  {intValue && intValue.general && !intValue.general.url ? (
42
42
  <p className="tableau-error">URL required</p>
43
- ) : intValue && intValue.general && !intValue.general.version ? (
44
- <p className="tableau-error">Version required</p>
45
43
  ) : tableauError ? (
46
44
  <p className="tableau-error">{tableauError}</p>
47
45
  ) : (
@@ -55,15 +53,6 @@ const VisualizationWidget = (props) => {
55
53
  let schema = Schema(config);
56
54
 
57
55
  React.useEffect(() => {
58
- if (!intValue?.general || !intValue?.general?.version) {
59
- setIntValue({
60
- ...intValue,
61
- general: {
62
- url: intValue?.general?.url,
63
- version: '',
64
- },
65
- });
66
- }
67
56
  if (!intValue?.options) {
68
57
  setIntValue({
69
58
  ...intValue,
@@ -118,9 +107,7 @@ const VisualizationWidget = (props) => {
118
107
  />
119
108
  </Grid.Column>
120
109
  <Grid.Column mobile={12} tablet={12} computer={7}>
121
- {(intValue &&
122
- intValue.general &&
123
- (!intValue.general.url || !intValue.general.version)) ||
110
+ {(intValue && intValue.general && !intValue.general.url) ||
124
111
  tableauError ? (
125
112
  <TableauNotDisplayed />
126
113
  ) : (
@@ -152,9 +139,7 @@ const VisualizationWidget = (props) => {
152
139
  </Modal.Actions>
153
140
  </Modal>
154
141
  )}
155
- {(intValue &&
156
- intValue.general &&
157
- (!intValue.general.url || !intValue.general.version)) ||
142
+ {(intValue && intValue.general && !intValue.general.url) ||
158
143
  tableauError ? (
159
144
  <TableauNotDisplayed />
160
145
  ) : (
@@ -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,
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 = {
@@ -186,7 +166,7 @@ export default (config) => {
186
166
  schemas: [
187
167
  {
188
168
  id: 'general',
189
- schema: generalSchema(config),
169
+ schema: generalSchema,
190
170
  },
191
171
  {
192
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;