@eeacms/volto-tableau 6.0.0 → 6.0.2
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 +37 -4
- package/Dockerfile +1 -1
- package/Jenkinsfile +153 -135
- package/Makefile +35 -2
- package/cypress.config.js +2 -2
- package/package.json +1 -1
- package/src/Blocks/EmbedTableauVisualization/View.jsx +8 -10
- package/src/Blocks/EmbedTableauVisualization/View.test.jsx +2 -2
- package/src/Blocks/EmbedTableauVisualization/schema.js +8 -4
- package/src/Tableau/Tableau.jsx +47 -26
- package/src/Utils/{Download/Download.jsx → Download.jsx} +31 -38
- package/src/Utils/index.js +2 -0
- package/src/Views/VisualizationView.jsx +4 -0
- package/src/Widgets/VisualizationViewWidget.jsx +6 -1
- package/src/Widgets/VisualizationViewWidget.test.jsx +33 -0
- package/src/Widgets/VisualizationWidget.jsx +16 -4
- package/src/Widgets/VisualizationWidget.test.jsx +45 -0
- package/src/Widgets/schema.js +2 -1
- package/src/helpers.js +28 -47
- package/src/hooks.js +22 -6
- package/src/less/tableau.less +8 -227
- package/src/less/tableau.variables +4 -3
- package/src/Utils/Download/Download.test.jsx +0 -22
- package/src/Utils/FigureNote/FigureNote.jsx +0 -43
- package/src/Utils/FigureNote/FigureNote.test.jsx +0 -25
- package/src/Utils/MoreInfoLink/MoreInfoLink.jsx +0 -22
- package/src/Utils/MoreInfoLink/MoreInfoLink.test.jsx +0 -24
- package/src/Utils/Share/Share.jsx +0 -69
- package/src/Utils/Sources/Sources.jsx +0 -67
- /package/src/Utils/{JsonCodeSnippet/JsonCodeSnippet.jsx → JsonCodeSnippet.jsx} +0 -0
|
@@ -29,7 +29,7 @@ describe('View', () => {
|
|
|
29
29
|
tableau_vis_url: 'http://localhost:3000/tableau-ct',
|
|
30
30
|
with_download: true,
|
|
31
31
|
with_more_info: true,
|
|
32
|
-
|
|
32
|
+
with_notes: true,
|
|
33
33
|
with_share: true,
|
|
34
34
|
};
|
|
35
35
|
|
|
@@ -39,6 +39,6 @@ describe('View', () => {
|
|
|
39
39
|
<View data={data} />
|
|
40
40
|
</Provider>,
|
|
41
41
|
);
|
|
42
|
-
expect(container.querySelector('.embed-
|
|
42
|
+
expect(container.querySelector('.embed-tableau')).toBeInTheDocument();
|
|
43
43
|
});
|
|
44
44
|
});
|
|
@@ -47,14 +47,17 @@ export default () => {
|
|
|
47
47
|
{
|
|
48
48
|
id: 'default',
|
|
49
49
|
title: 'Default',
|
|
50
|
+
fields: ['tableau_vis_url', 'tableau_height'],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'toolbar',
|
|
54
|
+
title: 'Toolbar',
|
|
50
55
|
fields: [
|
|
51
|
-
'
|
|
52
|
-
'with_note',
|
|
56
|
+
'with_notes',
|
|
53
57
|
'with_sources',
|
|
54
58
|
'with_more_info',
|
|
55
59
|
'with_download',
|
|
56
60
|
'with_share',
|
|
57
|
-
'tableau_height',
|
|
58
61
|
],
|
|
59
62
|
},
|
|
60
63
|
{
|
|
@@ -68,13 +71,14 @@ export default () => {
|
|
|
68
71
|
title: 'Tableau visualization',
|
|
69
72
|
widget: 'url',
|
|
70
73
|
},
|
|
71
|
-
|
|
74
|
+
with_notes: {
|
|
72
75
|
title: 'Show note',
|
|
73
76
|
type: 'boolean',
|
|
74
77
|
defaultValue: true,
|
|
75
78
|
},
|
|
76
79
|
with_sources: {
|
|
77
80
|
title: 'Show sources',
|
|
81
|
+
description: 'Will show sources set in this page Data provenance',
|
|
78
82
|
type: 'boolean',
|
|
79
83
|
defaultValue: true,
|
|
80
84
|
},
|
package/src/Tableau/Tableau.jsx
CHANGED
|
@@ -14,17 +14,21 @@ import isUndefined from 'lodash/isUndefined';
|
|
|
14
14
|
import cx from 'classnames';
|
|
15
15
|
import { Button } from 'semantic-ui-react';
|
|
16
16
|
import { Toast, Icon } from '@plone/volto/components';
|
|
17
|
+
import {
|
|
18
|
+
FigureNote,
|
|
19
|
+
Sources,
|
|
20
|
+
MoreInfo,
|
|
21
|
+
Share,
|
|
22
|
+
} from '@eeacms/volto-embed/Toolbar';
|
|
17
23
|
import { useTableau } from '@eeacms/volto-tableau/hooks';
|
|
18
|
-
import JsonCodeSnippet from '@eeacms/volto-tableau/Utils
|
|
19
|
-
|
|
20
|
-
import Sources from '@eeacms/volto-tableau/Utils/Sources/Sources';
|
|
21
|
-
import MoreInfoLink from '@eeacms/volto-tableau/Utils/MoreInfoLink/MoreInfoLink';
|
|
22
|
-
import Download from '@eeacms/volto-tableau/Utils/Download/Download';
|
|
23
|
-
import Share from '@eeacms/volto-tableau/Utils/Share/Share';
|
|
24
|
+
import { JsonCodeSnippet, Download } from '@eeacms/volto-tableau/Utils';
|
|
25
|
+
|
|
24
26
|
import { getSheetnames, getActiveSheetname, getDevice } from './helpers';
|
|
25
27
|
|
|
26
28
|
import resetSVG from '@plone/volto/icons/reset.svg';
|
|
27
29
|
|
|
30
|
+
import '@eeacms/volto-embed/Toolbar/styles.less';
|
|
31
|
+
|
|
28
32
|
const TableauDebug = ({ mode, data, vizState, url, version, clearData }) => {
|
|
29
33
|
const { loaded, error } = vizState;
|
|
30
34
|
const { filters = {}, parameters = {} } = data;
|
|
@@ -66,6 +70,7 @@ const TableauDebug = ({ mode, data, vizState, url, version, clearData }) => {
|
|
|
66
70
|
};
|
|
67
71
|
|
|
68
72
|
const Tableau = forwardRef((props, ref) => {
|
|
73
|
+
const tableauEl = useRef(null);
|
|
69
74
|
const vizEl = useRef(null);
|
|
70
75
|
const viz = useRef();
|
|
71
76
|
const vizState = useRef({});
|
|
@@ -74,6 +79,7 @@ const Tableau = forwardRef((props, ref) => {
|
|
|
74
79
|
const [loaded, setLoaded] = useState(false);
|
|
75
80
|
const [loading, setLoading] = useState(false);
|
|
76
81
|
const [error, setError] = useState(null);
|
|
82
|
+
const [mobile, setMobile] = useState(false);
|
|
77
83
|
const {
|
|
78
84
|
block,
|
|
79
85
|
data = {},
|
|
@@ -96,12 +102,12 @@ const Tableau = forwardRef((props, ref) => {
|
|
|
96
102
|
toolbarPosition = 'Top',
|
|
97
103
|
breakpointUrls = [],
|
|
98
104
|
tableau_vis_url,
|
|
99
|
-
|
|
100
|
-
with_sources,
|
|
101
|
-
with_more_info,
|
|
102
|
-
with_download,
|
|
103
|
-
with_share,
|
|
104
|
-
tableau_height
|
|
105
|
+
with_notes = true,
|
|
106
|
+
with_sources = true,
|
|
107
|
+
with_more_info = true,
|
|
108
|
+
with_download = true,
|
|
109
|
+
with_share = true,
|
|
110
|
+
tableau_height,
|
|
105
111
|
} = data;
|
|
106
112
|
const device = useMemo(
|
|
107
113
|
() => getDevice(breakpoints, screen.page?.width || Infinity),
|
|
@@ -327,6 +333,7 @@ const Tableau = forwardRef((props, ref) => {
|
|
|
327
333
|
}, [data]);
|
|
328
334
|
|
|
329
335
|
useEffect(() => {
|
|
336
|
+
// console.log('HERE trigger initiateViz', tableau, url);
|
|
330
337
|
if (!tableau) return;
|
|
331
338
|
if (url) {
|
|
332
339
|
disposeViz();
|
|
@@ -366,6 +373,18 @@ const Tableau = forwardRef((props, ref) => {
|
|
|
366
373
|
/* eslint-disable-next-line */
|
|
367
374
|
}, [sheetname]);
|
|
368
375
|
|
|
376
|
+
useEffect(() => {
|
|
377
|
+
if (!loading && tableauEl.current) {
|
|
378
|
+
const visWidth = tableauEl.current.offsetWidth;
|
|
379
|
+
|
|
380
|
+
if (visWidth < 600 && !mobile) {
|
|
381
|
+
setMobile(true);
|
|
382
|
+
} else if (visWidth >= 600 && mobile) {
|
|
383
|
+
setMobile(false);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}, [screen, mobile, loading]);
|
|
387
|
+
|
|
369
388
|
useImperativeHandle(
|
|
370
389
|
ref,
|
|
371
390
|
() => {
|
|
@@ -381,7 +400,7 @@ const Tableau = forwardRef((props, ref) => {
|
|
|
381
400
|
);
|
|
382
401
|
|
|
383
402
|
return (
|
|
384
|
-
<div className="tableau-wrapper">
|
|
403
|
+
<div className="tableau-wrapper" ref={tableauEl}>
|
|
385
404
|
{loading && (
|
|
386
405
|
<div className="tableau-loader">
|
|
387
406
|
<span>Loading...</span>
|
|
@@ -412,25 +431,27 @@ const Tableau = forwardRef((props, ref) => {
|
|
|
412
431
|
clearData={clearData}
|
|
413
432
|
/>
|
|
414
433
|
<div
|
|
415
|
-
style={{ height: tableau_height + 'px' }}
|
|
434
|
+
style={{ height: tableau_height ? tableau_height + 'px' : '100%' }}
|
|
416
435
|
className={cx('tableau', `tableau-${version}`, {
|
|
417
436
|
'tableau-autoscale': autoScale,
|
|
418
437
|
})}
|
|
419
438
|
ref={vizEl}
|
|
420
439
|
/>
|
|
421
|
-
|
|
422
|
-
<div className=
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
<
|
|
427
|
-
|
|
440
|
+
{loaded && (
|
|
441
|
+
<div className={cx('visualization-toolbar', { mobile })}>
|
|
442
|
+
<div className="left-col">
|
|
443
|
+
{with_notes && <FigureNote note={figure_note || []} />}
|
|
444
|
+
{with_sources && <Sources sources={sources} />}
|
|
445
|
+
{with_more_info && <MoreInfo href={tableau_vis_url || data.url} />}
|
|
446
|
+
</div>
|
|
447
|
+
<div className="right-col">
|
|
448
|
+
{with_download && loaded && <Download viz={viz.current} />}
|
|
449
|
+
{with_share && loaded && (
|
|
450
|
+
<Share href={tableau_vis_url || data.url} />
|
|
451
|
+
)}
|
|
452
|
+
</div>
|
|
428
453
|
</div>
|
|
429
|
-
|
|
430
|
-
{with_download && loaded && <Download viz={viz.current} />}
|
|
431
|
-
{with_share && loaded && <Share contentTypeLink={tableau_vis_url} />}
|
|
432
|
-
</div>
|
|
433
|
-
</div>
|
|
454
|
+
)}
|
|
434
455
|
</div>
|
|
435
456
|
);
|
|
436
457
|
});
|
|
@@ -3,37 +3,37 @@ import { Popup } from 'semantic-ui-react';
|
|
|
3
3
|
import cx from 'classnames';
|
|
4
4
|
|
|
5
5
|
const Download = ({ viz }) => {
|
|
6
|
-
const [
|
|
6
|
+
const [open, setOpen] = React.useState(false);
|
|
7
7
|
const popupRef = React.useRef();
|
|
8
8
|
|
|
9
9
|
return (
|
|
10
10
|
<Popup
|
|
11
|
-
popper={{ id: '
|
|
12
|
-
trigger={
|
|
13
|
-
<div className="tableau-download-container">
|
|
14
|
-
<button className={cx('tableau-download-button', { expanded })}>
|
|
15
|
-
Download <i class="ri-download-fill"></i>
|
|
16
|
-
</button>
|
|
17
|
-
</div>
|
|
18
|
-
}
|
|
11
|
+
popper={{ id: 'vis-toolbar-popup', className: 'download-popup' }}
|
|
19
12
|
position="bottom left"
|
|
20
13
|
on="click"
|
|
14
|
+
open={open}
|
|
21
15
|
onClose={() => {
|
|
22
|
-
|
|
16
|
+
setOpen(false);
|
|
23
17
|
}}
|
|
24
18
|
onOpen={() => {
|
|
25
|
-
|
|
19
|
+
setOpen(true);
|
|
26
20
|
}}
|
|
27
21
|
ref={popupRef}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
22
|
+
trigger={
|
|
23
|
+
<div className="tableau-download-container">
|
|
24
|
+
<button className={cx('trigger-button', { open })}>
|
|
25
|
+
<i className="ri-download-fill" />
|
|
26
|
+
Download
|
|
27
|
+
</button>
|
|
28
|
+
</div>
|
|
29
|
+
}
|
|
30
|
+
content={
|
|
31
|
+
<>
|
|
32
|
+
<div className="item">
|
|
33
|
+
<span className="label">Data formats</span>
|
|
34
|
+
<div className="types">
|
|
35
|
+
<div className="type">
|
|
35
36
|
<button
|
|
36
|
-
className="tableau-download-button tableau-format-download"
|
|
37
37
|
onClick={() => {
|
|
38
38
|
viz.showExportCrossTabDialog();
|
|
39
39
|
popupRef.current.triggerRef.current.click();
|
|
@@ -42,9 +42,8 @@ const Download = ({ viz }) => {
|
|
|
42
42
|
<span>CSV</span>
|
|
43
43
|
</button>
|
|
44
44
|
</div>
|
|
45
|
-
<div>
|
|
45
|
+
<div className="type">
|
|
46
46
|
<button
|
|
47
|
-
className="tableau-download-button tableau-format-download"
|
|
48
47
|
onClick={() => {
|
|
49
48
|
viz.exportCrossTabToExcel();
|
|
50
49
|
popupRef.current.triggerRef.current.click();
|
|
@@ -55,14 +54,11 @@ const Download = ({ viz }) => {
|
|
|
55
54
|
</div>
|
|
56
55
|
</div>
|
|
57
56
|
</div>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<div className="visualization-info">
|
|
63
|
-
<div>
|
|
57
|
+
<div className="item">
|
|
58
|
+
<span className="label">Image formats</span>
|
|
59
|
+
<div className="types">
|
|
60
|
+
<div className="type">
|
|
64
61
|
<button
|
|
65
|
-
className="tableau-download-button tableau-format-download"
|
|
66
62
|
onClick={() => {
|
|
67
63
|
viz.showExportImageDialog();
|
|
68
64
|
popupRef.current.triggerRef.current.click();
|
|
@@ -73,14 +69,11 @@ const Download = ({ viz }) => {
|
|
|
73
69
|
</div>
|
|
74
70
|
</div>
|
|
75
71
|
</div>
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
<div className="visualization-info">
|
|
81
|
-
<div>
|
|
72
|
+
<div className="item">
|
|
73
|
+
<span className="label">Other formats</span>
|
|
74
|
+
<div className="types">
|
|
75
|
+
<div className="type">
|
|
82
76
|
<button
|
|
83
|
-
className="tableau-download-button tableau-format-download"
|
|
84
77
|
onClick={() => {
|
|
85
78
|
viz.showExportPDFDialog();
|
|
86
79
|
popupRef.current.triggerRef.current.click();
|
|
@@ -91,9 +84,9 @@ const Download = ({ viz }) => {
|
|
|
91
84
|
</div>
|
|
92
85
|
</div>
|
|
93
86
|
</div>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
87
|
+
</>
|
|
88
|
+
}
|
|
89
|
+
/>
|
|
97
90
|
);
|
|
98
91
|
};
|
|
99
92
|
|
|
@@ -3,14 +3,19 @@ import config from '@plone/volto/registry';
|
|
|
3
3
|
|
|
4
4
|
export default function VisualizationViewWidget(props) {
|
|
5
5
|
const { value = {} } = props;
|
|
6
|
+
|
|
6
7
|
return (
|
|
7
8
|
<Tableau
|
|
8
9
|
data={{
|
|
9
10
|
...value,
|
|
11
|
+
with_notes: false,
|
|
12
|
+
with_sources: false,
|
|
13
|
+
with_more_info: true,
|
|
14
|
+
with_share: true,
|
|
10
15
|
with_download: true,
|
|
11
16
|
}}
|
|
12
17
|
breakpoints={
|
|
13
|
-
config.blocks.blocksConfig
|
|
18
|
+
config.blocks.blocksConfig?.embed_tableau_visualization?.breakpoints
|
|
14
19
|
}
|
|
15
20
|
/>
|
|
16
21
|
);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom/extend-expect';
|
|
4
|
+
import { Provider } from 'react-redux';
|
|
5
|
+
import configureStore from 'redux-mock-store';
|
|
6
|
+
import VisualizationViewWidget from './VisualizationViewWidget';
|
|
7
|
+
|
|
8
|
+
const mockStore = configureStore([]);
|
|
9
|
+
const store = mockStore({});
|
|
10
|
+
|
|
11
|
+
jest.mock('@plone/volto/components', () => ({
|
|
12
|
+
Icon: ({ children }) => <img alt="incon">{children}</img>,
|
|
13
|
+
Toast: ({ children }) => <p>{children}</p>,
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
describe('VisualizationViewWidget', () => {
|
|
17
|
+
it('should render the component', () => {
|
|
18
|
+
const data = {
|
|
19
|
+
url: 'http://localhost:3000/tableau-ct',
|
|
20
|
+
with_download: true,
|
|
21
|
+
with_more_info: true,
|
|
22
|
+
with_note: true,
|
|
23
|
+
with_share: true,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const { container } = render(
|
|
27
|
+
<Provider store={store}>
|
|
28
|
+
<VisualizationViewWidget data={data} />
|
|
29
|
+
</Provider>,
|
|
30
|
+
);
|
|
31
|
+
expect(container.querySelector('.tableau-wrapper')).toBeInTheDocument();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -72,11 +72,18 @@ const VisualizationWidget = (props) => {
|
|
|
72
72
|
>
|
|
73
73
|
<Tableau
|
|
74
74
|
ref={viz}
|
|
75
|
-
data={
|
|
75
|
+
data={{
|
|
76
|
+
...(value || {}),
|
|
77
|
+
with_notes: false,
|
|
78
|
+
with_sources: false,
|
|
79
|
+
with_more_info: false,
|
|
80
|
+
with_share: false,
|
|
81
|
+
with_download: false,
|
|
82
|
+
}}
|
|
76
83
|
mode="edit"
|
|
77
84
|
breakpoints={
|
|
78
|
-
config.blocks.blocksConfig
|
|
79
|
-
|
|
85
|
+
config.blocks.blocksConfig?.embed_tableau_visualization
|
|
86
|
+
?.breakpoints
|
|
80
87
|
}
|
|
81
88
|
extraOptions={extraOptions}
|
|
82
89
|
setVizState={setVizState}
|
|
@@ -116,9 +123,14 @@ const VisualizationWidget = (props) => {
|
|
|
116
123
|
data={{
|
|
117
124
|
...props.value,
|
|
118
125
|
autoScale: true,
|
|
126
|
+
with_notes: false,
|
|
127
|
+
with_sources: false,
|
|
128
|
+
with_more_info: false,
|
|
129
|
+
with_share: false,
|
|
130
|
+
with_download: false,
|
|
119
131
|
}}
|
|
120
132
|
breakpoints={
|
|
121
|
-
config.blocks.blocksConfig
|
|
133
|
+
config.blocks.blocksConfig?.embed_tableau_visualization?.breakpoints
|
|
122
134
|
}
|
|
123
135
|
extraOptions={extraOptions}
|
|
124
136
|
/>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom/extend-expect';
|
|
4
|
+
import { Provider } from 'react-redux';
|
|
5
|
+
import configureStore from 'redux-mock-store';
|
|
6
|
+
import VisualizationWidget from './VisualizationWidget';
|
|
7
|
+
|
|
8
|
+
const mockStore = configureStore([]);
|
|
9
|
+
const store = mockStore({});
|
|
10
|
+
|
|
11
|
+
jest.mock('@plone/volto/components', () => ({
|
|
12
|
+
FormFieldWrapper: jest.fn(({ children }) => <>{children}</>),
|
|
13
|
+
InlineForm: jest.fn(() => <div>Mocked InlineForm</div>),
|
|
14
|
+
Icon: ({ children }) => <img alt="incon">{children}</img>,
|
|
15
|
+
Toast: ({ children }) => <p>{children}</p>,
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
describe('VisualizationWidget', () => {
|
|
19
|
+
it('should render the component', () => {
|
|
20
|
+
const data = {
|
|
21
|
+
value: {
|
|
22
|
+
url: 'http://localhost:3000/tableau-ct',
|
|
23
|
+
with_download: true,
|
|
24
|
+
with_more_info: true,
|
|
25
|
+
with_note: true,
|
|
26
|
+
with_share: true,
|
|
27
|
+
hideTabs: false,
|
|
28
|
+
staticParameters: [
|
|
29
|
+
{
|
|
30
|
+
'@id': '1f050748-c020-4a48-8109-e99a25bf558d',
|
|
31
|
+
field: 'Tableau field',
|
|
32
|
+
value: 'Tableau value',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const { container } = render(
|
|
39
|
+
<Provider store={store}>
|
|
40
|
+
<VisualizationWidget {...data} />
|
|
41
|
+
</Provider>,
|
|
42
|
+
);
|
|
43
|
+
expect(container.querySelector('.tableau-wrapper')).toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
});
|
package/src/Widgets/schema.js
CHANGED
|
@@ -38,7 +38,8 @@ const staticParameters = {
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
const breakpointUrlSchema = (config) => {
|
|
41
|
-
const breakpoints =
|
|
41
|
+
const breakpoints =
|
|
42
|
+
config.blocks.blocksConfig?.tableau_block?.breakpoints || {};
|
|
42
43
|
|
|
43
44
|
return {
|
|
44
45
|
title: 'URL',
|
package/src/helpers.js
CHANGED
|
@@ -1,28 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
existingScript
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
};
|
|
1
|
+
export async function loadTableauScript(version) {
|
|
2
|
+
return new Promise((resolve) => {
|
|
3
|
+
if (!__CLIENT__) {
|
|
4
|
+
resolve();
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
const source = `https://public.tableau.com/javascripts/api/tableau-${version}.min.js`;
|
|
8
|
+
const existingScript = document.getElementById(`tableauJS-${version}`);
|
|
9
|
+
const existingScriptSource =
|
|
10
|
+
existingScript && existingScript.getAttribute('src');
|
|
11
|
+
// Replace script loaded on each version change
|
|
12
|
+
if (existingScript && existingScriptSource !== source) {
|
|
13
|
+
existingScript.setAttribute('src', source);
|
|
14
|
+
}
|
|
15
|
+
if (!existingScript) {
|
|
16
|
+
const script = document.createElement('script');
|
|
17
|
+
script.src = source;
|
|
18
|
+
script.id = `tableauJS-${version}`;
|
|
19
|
+
document.body.appendChild(script);
|
|
20
|
+
script.addEventListener('load', () => {
|
|
21
|
+
window[`tableau_${version}`] = window.tableau;
|
|
22
|
+
resolve(window[`tableau_${version}`]);
|
|
23
|
+
});
|
|
24
|
+
} else {
|
|
25
|
+
resolve(window[`tableau_${version}`]);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
26
29
|
|
|
27
30
|
// Script url for each version. In case you might need to add them in the load balancer
|
|
28
31
|
// https://public.tableau.com/javascripts/api/tableau-2.8.0.min.js
|
|
@@ -34,25 +37,3 @@ export const loadTableauScript = (callback, version) => {
|
|
|
34
37
|
// https://public.tableau.com/javascripts/api/tableau-2.2.2.min.js
|
|
35
38
|
// https://public.tableau.com/javascripts/api/tableau-2.1.2.min.js
|
|
36
39
|
// https://public.tableau.com/javascripts/api/tableau-2.0.3.min.js
|
|
37
|
-
|
|
38
|
-
export const useCopyToClipboard = (text) => {
|
|
39
|
-
const [copyStatus, setCopyStatus] = React.useState('inactive');
|
|
40
|
-
const copy = React.useCallback(() => {
|
|
41
|
-
navigator.clipboard.writeText(text).then(
|
|
42
|
-
() => setCopyStatus('copied'),
|
|
43
|
-
() => setCopyStatus('failed'),
|
|
44
|
-
);
|
|
45
|
-
}, [text]);
|
|
46
|
-
|
|
47
|
-
React.useEffect(() => {
|
|
48
|
-
if (copyStatus === 'inactive') {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const timeout = setTimeout(() => setCopyStatus('inactive'), 3000);
|
|
53
|
-
|
|
54
|
-
return () => clearTimeout(timeout);
|
|
55
|
-
}, [copyStatus]);
|
|
56
|
-
|
|
57
|
-
return [copyStatus, copy];
|
|
58
|
-
};
|
package/src/hooks.js
CHANGED
|
@@ -1,17 +1,33 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
import { loadTableauScript } from './helpers';
|
|
3
3
|
|
|
4
|
+
let clock;
|
|
5
|
+
|
|
6
|
+
const TIMEOUT = 10000;
|
|
7
|
+
|
|
4
8
|
export const useTableau = (version) => {
|
|
5
9
|
const [tableau, setTableau] = useState();
|
|
6
10
|
|
|
7
11
|
useEffect(() => {
|
|
8
|
-
loadTableauScript(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
loadTableauScript(version);
|
|
13
|
+
|
|
14
|
+
const startTime = new Date().getTime();
|
|
15
|
+
|
|
16
|
+
clock = setInterval(() => {
|
|
17
|
+
const tableauApi = window[`tableau_${version}`];
|
|
18
|
+
if (tableauApi) {
|
|
19
|
+
setTableau(tableauApi);
|
|
20
|
+
clearInterval(clock);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (new Date().getTime() - startTime > TIMEOUT) {
|
|
24
|
+
clearInterval(clock);
|
|
13
25
|
}
|
|
14
|
-
},
|
|
26
|
+
}, 100);
|
|
27
|
+
|
|
28
|
+
return () => {
|
|
29
|
+
clearInterval(clock);
|
|
30
|
+
};
|
|
15
31
|
}, [version]);
|
|
16
32
|
|
|
17
33
|
return tableau;
|