@eeacms/volto-tableau 6.0.0 → 6.0.1

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.
@@ -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/JsonCodeSnippet/JsonCodeSnippet';
19
- import FigureNote from '@eeacms/volto-tableau/Utils/FigureNote/FigureNote';
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;
@@ -96,12 +100,12 @@ const Tableau = forwardRef((props, ref) => {
96
100
  toolbarPosition = 'Top',
97
101
  breakpointUrls = [],
98
102
  tableau_vis_url,
99
- with_note,
100
- with_sources,
101
- with_more_info,
102
- with_download,
103
- with_share,
104
- tableau_height = '700',
103
+ with_note = true,
104
+ with_sources = true,
105
+ with_more_info = true,
106
+ with_download = true,
107
+ with_share = true,
108
+ tableau_height,
105
109
  } = data;
106
110
  const device = useMemo(
107
111
  () => getDevice(breakpoints, screen.page?.width || Infinity),
@@ -412,25 +416,27 @@ const Tableau = forwardRef((props, ref) => {
412
416
  clearData={clearData}
413
417
  />
414
418
  <div
415
- style={{ height: tableau_height + 'px' }}
419
+ style={{ height: tableau_height ? tableau_height + 'px' : '100%' }}
416
420
  className={cx('tableau', `tableau-${version}`, {
417
421
  'tableau-autoscale': autoScale,
418
422
  })}
419
423
  ref={vizEl}
420
424
  />
421
- <div className="visualization-info-container">
422
- <div className="visualization-info">
423
- {with_note && loaded && <FigureNote note={figure_note || []} />}
424
- {with_sources && loaded && <Sources sources={sources} />}
425
- {with_more_info && loaded && (
426
- <MoreInfoLink contentTypeLink={tableau_vis_url} />
427
- )}
425
+ {loaded && (
426
+ <div className="visualization-toolbar">
427
+ <div className="left-col">
428
+ {with_note && <FigureNote note={figure_note || []} />}
429
+ {with_sources && <Sources sources={sources} />}
430
+ {with_more_info && <MoreInfo href={tableau_vis_url || data.url} />}
431
+ </div>
432
+ <div className="right-col">
433
+ {with_download && loaded && <Download viz={viz.current} />}
434
+ {with_share && loaded && (
435
+ <Share href={tableau_vis_url || data.url} />
436
+ )}
437
+ </div>
428
438
  </div>
429
- <div className="visualization-info">
430
- {with_download && loaded && <Download viz={viz.current} />}
431
- {with_share && loaded && <Share contentTypeLink={tableau_vis_url} />}
432
- </div>
433
- </div>
439
+ )}
434
440
  </div>
435
441
  );
436
442
  });
@@ -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 [expanded, setExpanded] = React.useState(false);
6
+ const [open, setOpen] = React.useState(false);
7
7
  const popupRef = React.useRef();
8
8
 
9
9
  return (
10
10
  <Popup
11
- popper={{ id: 'tableau-download-popup' }}
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
- setExpanded(false);
16
+ setOpen(false);
23
17
  }}
24
18
  onOpen={() => {
25
- setExpanded(true);
19
+ setOpen(true);
26
20
  }}
27
21
  ref={popupRef}
28
- >
29
- <ul className="no-bullets">
30
- <li>
31
- Data formats
32
- <div className="visualization-wrapper">
33
- <div className="visualization-info">
34
- <div>
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
- </li>
59
- <li>
60
- Image formats
61
- <div className="visualization-wrapper">
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
- </li>
77
- <li>
78
- Other formats
79
- <div className="visualization-wrapper">
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
- </li>
95
- </ul>
96
- </Popup>
87
+ </>
88
+ }
89
+ />
97
90
  );
98
91
  };
99
92
 
@@ -0,0 +1,2 @@
1
+ export { default as Download } from './Download';
2
+ export { default as JsonCodeSnippet } from './JsonCodeSnippet';
@@ -17,6 +17,10 @@ const VisualizationView = (props) => {
17
17
  <Tableau
18
18
  data={{
19
19
  ...tableau_visualization,
20
+ with_note: false,
21
+ with_sources: false,
22
+ with_more_info: false,
23
+ with_share: false,
20
24
  with_download: true,
21
25
  }}
22
26
  breakpoints={
@@ -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_note: 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.embed_tableau_visualization.breakpoints
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={value}
75
+ data={{
76
+ ...(value || {}),
77
+ with_note: 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.embed_tableau_visualization
79
- .breakpoints
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_note: 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.embed_tableau_visualization.breakpoints
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
+ });
@@ -38,7 +38,8 @@ const staticParameters = {
38
38
  };
39
39
 
40
40
  const breakpointUrlSchema = (config) => {
41
- const breakpoints = config.blocks.blocksConfig.tableau_block.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,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  export const loadTableauScript = (callback, version) => {
4
2
  if (!__CLIENT__) return;
5
3
  const source = `https://public.tableau.com/javascripts/api/tableau-${version}.min.js`;
@@ -34,25 +32,3 @@ export const loadTableauScript = (callback, version) => {
34
32
  // https://public.tableau.com/javascripts/api/tableau-2.2.2.min.js
35
33
  // https://public.tableau.com/javascripts/api/tableau-2.1.2.min.js
36
34
  // 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
- };
@@ -1,22 +0,0 @@
1
- import React from 'react';
2
- import { render } from '@testing-library/react';
3
- import '@testing-library/jest-dom/extend-expect';
4
-
5
- import Download from './Download';
6
-
7
- window.URL.createObjectURL = jest.fn(() => 'test');
8
-
9
- jest.mock('@plone/volto/components', () => ({
10
- Icon: ({ children }) => <img alt="incon">{children}</img>,
11
- }));
12
-
13
- describe('Download', () => {
14
- const viz = {};
15
-
16
- it('should render the component', () => {
17
- const { container } = render(<Download viz={viz} />);
18
- expect(
19
- container.querySelector('.tableau-download-container'),
20
- ).toBeInTheDocument();
21
- });
22
- });
@@ -1,43 +0,0 @@
1
- import React from 'react';
2
- import cx from 'classnames';
3
- import { Popup } from 'semantic-ui-react';
4
- import {
5
- serializeNodes,
6
- serializeNodesToText,
7
- } from '@plone/volto-slate/editor/render';
8
- import { isArray } from 'lodash';
9
-
10
- export const serializeText = (note) => {
11
- if (!serializeNodesToText(note))
12
- return <p>There are no notes set for this visualization</p>;
13
- return isArray(note) ? serializeNodes(note) : note;
14
- };
15
-
16
- const FigureNote = ({ note = [] }) => {
17
- const [expanded, setExpanded] = React.useState(false);
18
-
19
- return (
20
- <div className="tableau-note-container">
21
- <Popup
22
- position="bottom left"
23
- popper={{ id: 'tableau-note-popup' }}
24
- trigger={
25
- <button className={cx('tableau-note-button', { expanded })}>
26
- Note
27
- </button>
28
- }
29
- on="click"
30
- onClose={() => {
31
- setExpanded(false);
32
- }}
33
- onOpen={() => {
34
- setExpanded(true);
35
- }}
36
- >
37
- <Popup.Content>{serializeText(note)}</Popup.Content>
38
- </Popup>
39
- </div>
40
- );
41
- };
42
-
43
- export default FigureNote;
@@ -1,25 +0,0 @@
1
- import React from 'react';
2
- import { render } from '@testing-library/react';
3
- import '@testing-library/jest-dom/extend-expect';
4
-
5
- import FigureNote from './FigureNote';
6
-
7
- window.URL.createObjectURL = jest.fn(() => 'test');
8
-
9
- const slateEditor = require('@plone/volto-slate/editor/render');
10
- slateEditor.serializeNodes = jest.fn();
11
-
12
- jest.mock('@plone/volto-slate/editor/render', () => ({
13
- serializeNodesToText: ({ note = [] }) => note,
14
- }));
15
-
16
- describe('FigureNote', () => {
17
- const note = [];
18
-
19
- it('should render the component', () => {
20
- const { container } = render(<FigureNote note={note} />);
21
- expect(
22
- container.querySelector('.tableau-note-container'),
23
- ).toBeInTheDocument();
24
- });
25
- });
@@ -1,22 +0,0 @@
1
- import React from 'react';
2
- import cx from 'classnames';
3
- import { UniversalLink } from '@plone/volto/components';
4
-
5
- const Link = ({ children, ...props }) => {
6
- if (props.href) {
7
- return <UniversalLink {...props}>{children}</UniversalLink>;
8
- }
9
- return <span {...props}>{children}</span>;
10
- };
11
-
12
- const MoreInfoLink = ({ contentTypeLink }) => {
13
- return (
14
- <Link href={contentTypeLink}>
15
- <button className={cx('tableau-more-info-button')}>
16
- More info <i class="ri-external-link-line"></i>
17
- </button>
18
- </Link>
19
- );
20
- };
21
-
22
- export default MoreInfoLink;
@@ -1,24 +0,0 @@
1
- import React from 'react';
2
- import { render } from '@testing-library/react';
3
- import '@testing-library/jest-dom/extend-expect';
4
-
5
- import MoreInfoLink from './MoreInfoLink';
6
-
7
- window.URL.createObjectURL = jest.fn(() => 'test');
8
-
9
- jest.mock('@plone/volto/components', () => ({
10
- UniversalLink: ({ children }) => <div>{children}</div>,
11
- }));
12
-
13
- describe('MoreInfoLink', () => {
14
- const contentTypeLink = '/tableau-content-type';
15
-
16
- it('should render the component', () => {
17
- const { container } = render(
18
- <MoreInfoLink contentTypeLink={contentTypeLink} />,
19
- );
20
- expect(
21
- container.querySelector('.tableau-more-info-button'),
22
- ).toBeInTheDocument();
23
- });
24
- });
@@ -1,69 +0,0 @@
1
- import React from 'react';
2
- import { Popup, Input, Button } from 'semantic-ui-react';
3
- import { useCopyToClipboard } from '../../helpers.js';
4
- import cx from 'classnames';
5
-
6
- const Share = ({ contentTypeLink = '' }) => {
7
- const [expanded, setExpanded] = React.useState(false);
8
- const popupRef = React.useRef();
9
-
10
- const CopyUrlButton = ({ className, url, buttonText }) => {
11
- const [copyUrlStatus, copyUrl] = useCopyToClipboard(url);
12
-
13
- if (copyUrlStatus === 'copied') {
14
- buttonText = 'Copied!';
15
- } else if (copyUrlStatus === 'failed') {
16
- buttonText = 'Copy failed. Please try again.';
17
- }
18
-
19
- return (
20
- <Button
21
- primary
22
- onClick={copyUrl}
23
- className={cx('copy-button', className)}
24
- >
25
- {buttonText}
26
- </Button>
27
- );
28
- };
29
-
30
- return (
31
- <Popup
32
- popper={{ id: 'tableau-share-popup' }}
33
- trigger={
34
- <div className="tableau-share-container">
35
- <button className={cx('tableau-share-button', { expanded })}>
36
- <span>Share</span>
37
- <i class="ri-share-fill"></i>
38
- </button>
39
- </div>
40
- }
41
- position="bottom left"
42
- on="click"
43
- onClose={() => {
44
- setExpanded(false);
45
- }}
46
- onOpen={() => {
47
- setExpanded(true);
48
- }}
49
- ref={popupRef}
50
- >
51
- <div>
52
- <span className="tableau-share-popup-text">Copy link</span>
53
- <div className="tableau-share-popup-container">
54
- <Input
55
- className="tableau-share-link"
56
- defaultValue={contentTypeLink}
57
- />
58
- <CopyUrlButton
59
- className="tableau-copy-button"
60
- url={contentTypeLink}
61
- buttonText="Copy"
62
- />
63
- </div>
64
- </div>
65
- </Popup>
66
- );
67
- };
68
-
69
- export default Share;