@eeacms/volto-tableau 5.0.1 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,7 +4,48 @@ 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
- ### [5.0.1](https://github.com/eea/volto-tableau/compare/5.0.0...5.0.1) - 10 October 2023
7
+ ### [6.0.0](https://github.com/eea/volto-tableau/compare/5.0.2...6.0.0) - 30 October 2023
8
+
9
+ #### :house: Documentation changes
10
+
11
+ - docs: add demo gif [ana-oprea - [`a9ccb11`](https://github.com/eea/volto-tableau/commit/a9ccb1194ba6bcac99649b586113fe4f7c26a7eb)]
12
+
13
+ #### :hammer_and_wrench: Others
14
+
15
+ - Release 6.0.0 [Laszlo Cseh - [`576ce51`](https://github.com/eea/volto-tableau/commit/576ce5199f9021c33c404d20bbbce43192715c80)]
16
+ ### [5.0.2](https://github.com/eea/volto-tableau/compare/5.0.1...5.0.2) - 24 October 2023
17
+
18
+ #### :rocket: New Features
19
+
20
+ - feat: share option, design improvements [dana-cfc4 - [`a8d7ba1`](https://github.com/eea/volto-tableau/commit/a8d7ba18753451e2d9aaff3ecb3704a92e47681f)]
21
+ - feat: redesign download options [dana-cfc4 - [`96f2f3a`](https://github.com/eea/volto-tableau/commit/96f2f3adbdccf3629f95a5222ebfd2eaf3bc1b5c)]
22
+
23
+ #### :bug: Bug Fixes
24
+
25
+ - fix: format variables [dana-cfc4 - [`d9a6b41`](https://github.com/eea/volto-tableau/commit/d9a6b41ab532772c23d2df45dca8fb60f40df9d8)]
26
+ - fix: define @grey-1 less variable [dana-cfc4 - [`048c3a3`](https://github.com/eea/volto-tableau/commit/048c3a375923e6a34fa00ccdc63c59040850b5b8)]
27
+ - fix: align sources with other viz [dana-cfc4 - [`b0e38bd`](https://github.com/eea/volto-tableau/commit/b0e38bd2d83e1148e4f706cc9448ec9361bfae6c)]
28
+ - fix: same background colour for popups, remove background color for embed tableau [dana-cfc4 - [`bb73a31`](https://github.com/eea/volto-tableau/commit/bb73a310311882cdd82c9a7d22c57374d563557b)]
29
+ - fix: fix case when notes don't exist [dana-cfc4 - [`a204dc3`](https://github.com/eea/volto-tableau/commit/a204dc31f8b69f92885ae0056ea8596c32ae315e)]
30
+ - fix: volto version 16 [dana-cfc4 - [`b079f47`](https://github.com/eea/volto-tableau/commit/b079f47b94cb23d366f972096ecca645e26463bb)]
31
+ - fix: display message when notes don't exist [dana-cfc4 - [`c8943db`](https://github.com/eea/volto-tableau/commit/c8943db5b7c01d4a71ec65d9953cb1755046edc3)]
32
+ - fix: display figure note [dana-cfc4 - [`f8b4e6d`](https://github.com/eea/volto-tableau/commit/f8b4e6dd257aa64956f7e01a70c10a95eb48546d)]
33
+ - fix: lint [dana-cfc4 - [`7ec0cd1`](https://github.com/eea/volto-tableau/commit/7ec0cd134ef66ee58e532b987900c7c43f5aae1c)]
34
+ - fix: fix stylelint [dana-cfc4 - [`1b39f16`](https://github.com/eea/volto-tableau/commit/1b39f160b3b82716b7c568811ecd243bc35dc982)]
35
+
36
+ #### :house: Internal changes
37
+
38
+ - chore: husky, lint-staged use fixed versions [valentinab25 - [`f7ea803`](https://github.com/eea/volto-tableau/commit/f7ea803e032e27eab2193c4424d05afbca5fb6bc)]
39
+ - chore: display sources for block [dana-cfc4 - [`d064508`](https://github.com/eea/volto-tableau/commit/d0645087ecf79af73b3e69d9263229b82e339b37)]
40
+ - chore: display figureNote, add more tests [dana-cfc4 - [`7c8deb9`](https://github.com/eea/volto-tableau/commit/7c8deb94f721f2efe9a1373e162831a791cedf88)]
41
+ - chore: add more tests [dana-cfc4 - [`bf463a9`](https://github.com/eea/volto-tableau/commit/bf463a9223748506ed77d4e632d8a7a2b4eb5a13)]
42
+ - chore: add tests [dana-cfc4 - [`28cad5a`](https://github.com/eea/volto-tableau/commit/28cad5afebef69f8bf2c01531d4cae0ee7260b2f)]
43
+ - chore: add notes and more info, remove sources and share -refs#257682 [dana-cfc4 - [`23127dc`](https://github.com/eea/volto-tableau/commit/23127dcb1a9d682920f1fd8e24fd0f494f7aa8d1)]
44
+
45
+ #### :hammer_and_wrench: Others
46
+
47
+ - Fix maxVizResizeAttempts error [laszlocseh - [`66fa0e2`](https://github.com/eea/volto-tableau/commit/66fa0e2c9d4860373af107470158b68df4fd793f)]
48
+ ### [5.0.1](https://github.com/eea/volto-tableau/compare/5.0.0...5.0.1) - 12 October 2023
8
49
 
9
50
  #### :house: Internal changes
10
51
 
package/README.md CHANGED
@@ -20,6 +20,8 @@
20
20
 
21
21
  Registers a VisualizationView component for a content type named 'tableau_visualization'.
22
22
 
23
+ ![Tableau](https://raw.githubusercontent.com/eea/volto-tableau/master/docs/volto-tableau.gif)
24
+
23
25
  ## Getting started
24
26
 
25
27
  ### Try volto-tableau with Docker
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-tableau",
3
- "version": "5.0.1",
3
+ "version": "6.0.0",
4
4
  "description": "@eeacms/volto-tableau: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -30,8 +30,8 @@
30
30
  "@cypress/code-coverage": "^3.9.5",
31
31
  "@plone/scripts": "*",
32
32
  "babel-plugin-transform-class-properties": "^6.24.1",
33
- "husky": "*",
34
- "lint-staged": "*",
33
+ "husky": "^8.0.3",
34
+ "lint-staged": "^14.0.1",
35
35
  "md5": "^2.3.0"
36
36
  },
37
37
  "lint-staged": {
@@ -10,9 +10,17 @@ import { compose } from 'redux';
10
10
 
11
11
  const View = (props) => {
12
12
  const data = props.data;
13
- const { with_sources = true, with_download = true, with_share = true } = data;
14
- const { data_provenance, tableau_visualization } =
13
+ const {
14
+ with_note = true,
15
+ with_sources = true,
16
+ with_more_info = true,
17
+ with_download = true,
18
+ with_share = true,
19
+ tableau_height = '700',
20
+ } = data;
21
+ const { figure_note = [], data_provenance = {}, tableau_visualization } =
15
22
  props.tableau_visualization_data || {};
23
+
16
24
  const tableau_vis_url = flattenToAppURL(data.tableau_vis_url || '');
17
25
 
18
26
  React.useEffect(() => {
@@ -40,10 +48,15 @@ const View = (props) => {
40
48
  <Tableau
41
49
  data={{
42
50
  ...tableau_visualization,
51
+ with_note,
43
52
  with_sources,
53
+ with_more_info,
44
54
  with_download,
45
55
  with_share,
56
+ tableau_height,
57
+ tableau_vis_url,
46
58
  }}
59
+ figure_note={figure_note}
47
60
  sources={data_provenance.data || []}
48
61
  />
49
62
  )}
@@ -0,0 +1,44 @@
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 View from './View';
7
+
8
+ const mockStore = configureStore([]);
9
+ const store = mockStore({ content: { subrequests: [] } });
10
+
11
+ window.URL.createObjectURL = jest.fn(() => 'test');
12
+
13
+ jest.mock('@eeacms/volto-embed', () => ({
14
+ PrivacyProtection: ({ children }) => <div>{children}</div>,
15
+ }));
16
+
17
+ jest.mock('@plone/volto/components', () => ({
18
+ Icon: ({ children }) => <img alt="incon">{children}</img>,
19
+ }));
20
+
21
+ jest.mock('@plone/volto/components', () => ({
22
+ Toast: ({ children }) => <p>{children}</p>,
23
+ }));
24
+
25
+ describe('View', () => {
26
+ const data = {
27
+ '@type': 'embed_tableau_visualization',
28
+ dataprotection: {},
29
+ tableau_vis_url: 'http://localhost:3000/tableau-ct',
30
+ with_download: true,
31
+ with_more_info: true,
32
+ with_note: true,
33
+ with_share: true,
34
+ };
35
+
36
+ it('should render the component', () => {
37
+ const { container } = render(
38
+ <Provider store={store}>
39
+ <View data={data} />
40
+ </Provider>,
41
+ );
42
+ expect(container.querySelector('.embed-container')).toBeInTheDocument();
43
+ });
44
+ });
@@ -49,9 +49,12 @@ export default () => {
49
49
  title: 'Default',
50
50
  fields: [
51
51
  'tableau_vis_url',
52
+ 'with_note',
52
53
  'with_sources',
54
+ 'with_more_info',
53
55
  'with_download',
54
56
  'with_share',
57
+ 'tableau_height',
55
58
  ],
56
59
  },
57
60
  {
@@ -65,6 +68,21 @@ export default () => {
65
68
  title: 'Tableau visualization',
66
69
  widget: 'url',
67
70
  },
71
+ with_note: {
72
+ title: 'Show note',
73
+ type: 'boolean',
74
+ defaultValue: true,
75
+ },
76
+ with_sources: {
77
+ title: 'Show sources',
78
+ type: 'boolean',
79
+ defaultValue: true,
80
+ },
81
+ with_more_info: {
82
+ title: 'Show more info',
83
+ type: 'boolean',
84
+ defaultValue: true,
85
+ },
68
86
  with_download: {
69
87
  title: 'Show download button',
70
88
  type: 'boolean',
@@ -75,10 +93,10 @@ export default () => {
75
93
  type: 'boolean',
76
94
  defaultValue: true,
77
95
  },
78
- with_sources: {
79
- title: 'Show sources',
80
- type: 'boolean',
81
- defaultValue: true,
96
+ tableau_height: {
97
+ title: 'Height',
98
+ type: 'text',
99
+ defaultValue: '700',
82
100
  },
83
101
  dataprotection: {
84
102
  widget: 'object',
@@ -16,7 +16,9 @@ import { Button } from 'semantic-ui-react';
16
16
  import { Toast, Icon } from '@plone/volto/components';
17
17
  import { useTableau } from '@eeacms/volto-tableau/hooks';
18
18
  import JsonCodeSnippet from '@eeacms/volto-tableau/Utils/JsonCodeSnippet/JsonCodeSnippet';
19
+ import FigureNote from '@eeacms/volto-tableau/Utils/FigureNote/FigureNote';
19
20
  import Sources from '@eeacms/volto-tableau/Utils/Sources/Sources';
21
+ import MoreInfoLink from '@eeacms/volto-tableau/Utils/MoreInfoLink/MoreInfoLink';
20
22
  import Download from '@eeacms/volto-tableau/Utils/Download/Download';
21
23
  import Share from '@eeacms/volto-tableau/Utils/Share/Share';
22
24
  import { getSheetnames, getActiveSheetname, getDevice } from './helpers';
@@ -80,8 +82,9 @@ const Tableau = forwardRef((props, ref) => {
80
82
  extraOptions = {},
81
83
  mode = 'view',
82
84
  screen = {},
83
- version = '2.8.0',
85
+ figure_note = [],
84
86
  sources,
87
+ version = '2.8.0',
85
88
  setVizState,
86
89
  onChangeBlock,
87
90
  } = props;
@@ -92,11 +95,14 @@ const Tableau = forwardRef((props, ref) => {
92
95
  sheetname = '',
93
96
  toolbarPosition = 'Top',
94
97
  breakpointUrls = [],
98
+ tableau_vis_url,
99
+ with_note,
95
100
  with_sources,
101
+ with_more_info,
96
102
  with_download,
97
103
  with_share,
104
+ tableau_height = '700',
98
105
  } = data;
99
-
100
106
  const device = useMemo(
101
107
  () => getDevice(breakpoints, screen.page?.width || Infinity),
102
108
  [breakpoints, screen],
@@ -406,15 +412,24 @@ const Tableau = forwardRef((props, ref) => {
406
412
  clearData={clearData}
407
413
  />
408
414
  <div
415
+ style={{ height: tableau_height + 'px' }}
409
416
  className={cx('tableau', `tableau-${version}`, {
410
417
  'tableau-autoscale': autoScale,
411
418
  })}
412
419
  ref={vizEl}
413
420
  />
414
- <div className="tableau-info">
415
- {with_sources && loaded && <Sources sources={sources} />}
416
- {with_download && loaded && <Download viz={viz.current} />}
417
- {with_share && loaded && <Share viz={viz.current} />}
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
+ )}
428
+ </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>
418
433
  </div>
419
434
  </div>
420
435
  );
@@ -0,0 +1,30 @@
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 Tableau from './Tableau';
7
+
8
+ const mockStore = configureStore([]);
9
+ const store = mockStore({});
10
+
11
+ window.URL.createObjectURL = jest.fn(() => 'test');
12
+
13
+ jest.mock('@plone/volto/components', () => ({
14
+ Icon: ({ children }) => <img alt="incon">{children}</img>,
15
+ }));
16
+
17
+ jest.mock('@plone/volto/components', () => ({
18
+ Toast: ({ children }) => <p>{children}</p>,
19
+ }));
20
+
21
+ describe('Tableau', () => {
22
+ it('should render the component', () => {
23
+ const { container } = render(
24
+ <Provider store={store}>
25
+ <Tableau />
26
+ </Provider>,
27
+ );
28
+ expect(container.querySelector('.tableau-wrapper')).toBeInTheDocument();
29
+ });
30
+ });
@@ -1,8 +1,6 @@
1
1
  import React from 'react';
2
- import { Button, Popup } from 'semantic-ui-react';
2
+ import { Popup } from 'semantic-ui-react';
3
3
  import cx from 'classnames';
4
- import { Icon } from '@plone/volto/components';
5
- import downloadSVG from '@eeacms/volto-tableau/icons/download.svg';
6
4
 
7
5
  const Download = ({ viz }) => {
8
6
  const [expanded, setExpanded] = React.useState(false);
@@ -14,8 +12,7 @@ const Download = ({ viz }) => {
14
12
  trigger={
15
13
  <div className="tableau-download-container">
16
14
  <button className={cx('tableau-download-button', { expanded })}>
17
- <span>Download data</span>
18
- <Icon name={downloadSVG} size="24px" />
15
+ Download <i class="ri-download-fill"></i>
19
16
  </button>
20
17
  </div>
21
18
  }
@@ -29,42 +26,73 @@ const Download = ({ viz }) => {
29
26
  }}
30
27
  ref={popupRef}
31
28
  >
32
- <Button
33
- className="primary"
34
- onClick={() => {
35
- viz.showExportImageDialog();
36
- popupRef.current.triggerRef.current.click();
37
- }}
38
- >
39
- Image
40
- </Button>
41
- <Button
42
- className="primary"
43
- onClick={() => {
44
- viz.showExportPDFDialog();
45
- popupRef.current.triggerRef.current.click();
46
- }}
47
- >
48
- PDF
49
- </Button>
50
- <Button
51
- className="primary"
52
- onClick={() => {
53
- viz.showExportCrossTabDialog();
54
- popupRef.current.triggerRef.current.click();
55
- }}
56
- >
57
- CSV
58
- </Button>
59
- <Button
60
- className="primary"
61
- onClick={() => {
62
- viz.exportCrossTabToExcel();
63
- popupRef.current.triggerRef.current.click();
64
- }}
65
- >
66
- Excel
67
- </Button>
29
+ <ul className="no-bullets">
30
+ <li>
31
+ Data formats
32
+ <div className="visualization-wrapper">
33
+ <div className="visualization-info">
34
+ <div>
35
+ <button
36
+ className="tableau-download-button tableau-format-download"
37
+ onClick={() => {
38
+ viz.showExportCrossTabDialog();
39
+ popupRef.current.triggerRef.current.click();
40
+ }}
41
+ >
42
+ <span>CSV</span>
43
+ </button>
44
+ </div>
45
+ <div>
46
+ <button
47
+ className="tableau-download-button tableau-format-download"
48
+ onClick={() => {
49
+ viz.exportCrossTabToExcel();
50
+ popupRef.current.triggerRef.current.click();
51
+ }}
52
+ >
53
+ <span>Excel</span>
54
+ </button>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </li>
59
+ <li>
60
+ Image formats
61
+ <div className="visualization-wrapper">
62
+ <div className="visualization-info">
63
+ <div>
64
+ <button
65
+ className="tableau-download-button tableau-format-download"
66
+ onClick={() => {
67
+ viz.showExportImageDialog();
68
+ popupRef.current.triggerRef.current.click();
69
+ }}
70
+ >
71
+ <span>PNG</span>
72
+ </button>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </li>
77
+ <li>
78
+ Other formats
79
+ <div className="visualization-wrapper">
80
+ <div className="visualization-info">
81
+ <div>
82
+ <button
83
+ className="tableau-download-button tableau-format-download"
84
+ onClick={() => {
85
+ viz.showExportPDFDialog();
86
+ popupRef.current.triggerRef.current.click();
87
+ }}
88
+ >
89
+ <span>PDF</span>
90
+ </button>
91
+ </div>
92
+ </div>
93
+ </div>
94
+ </li>
95
+ </ul>
68
96
  </Popup>
69
97
  );
70
98
  };
@@ -0,0 +1,22 @@
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
+ });
@@ -0,0 +1,43 @@
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;
@@ -0,0 +1,25 @@
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
+ });
@@ -0,0 +1,22 @@
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;
@@ -0,0 +1,24 @@
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,20 +1,68 @@
1
1
  import React from 'react';
2
- import { Icon } from '@plone/volto/components';
3
- import shareSVG from '@plone/volto/icons/share.svg';
2
+ import { Popup, Input, Button } from 'semantic-ui-react';
3
+ import { useCopyToClipboard } from '../../helpers.js';
4
+ import cx from 'classnames';
4
5
 
5
- const Share = ({ viz }) => {
6
- return (
7
- <div className="tableau-share-container">
8
- <button
9
- className="tableau-share-button"
10
- onClick={() => {
11
- viz.showShareDialog();
12
- }}
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)}
13
24
  >
14
- <span>Share</span>
15
- <Icon name={shareSVG} size="24px" />
16
- </button>
17
- </div>
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>
18
66
  );
19
67
  };
20
68
 
@@ -14,11 +14,12 @@ const Source = ({ source }) => {
14
14
  return (
15
15
  <>
16
16
  <Link className="embed-sources-param-title" href={source.link}>
17
- {source.title},{' '}
18
- <span className="embed-sources-param-description">
19
- {source.organisation}
20
- </span>
17
+ {source.title}
21
18
  </Link>
19
+ ,
20
+ <span className="embed-sources-param-description">
21
+ {source.organisation}
22
+ </span>
22
23
  </>
23
24
  );
24
25
  };
@@ -7,7 +7,7 @@ import Tableau from '@eeacms/volto-tableau/Tableau/Tableau';
7
7
 
8
8
  const VisualizationView = (props) => {
9
9
  const { content = {} } = props;
10
- const { tableau_visualization = {}, data_provenance = {} } = content;
10
+ const { tableau_visualization = {} } = content;
11
11
 
12
12
  return (
13
13
  <Container id="page-document">
@@ -17,11 +17,8 @@ const VisualizationView = (props) => {
17
17
  <Tableau
18
18
  data={{
19
19
  ...tableau_visualization,
20
- with_sources: true,
21
20
  with_download: true,
22
- with_share: true,
23
21
  }}
24
- sources={data_provenance.data || []}
25
22
  breakpoints={
26
23
  config.blocks.blocksConfig.embed_tableau_visualization.breakpoints
27
24
  }
@@ -7,11 +7,8 @@ export default function VisualizationViewWidget(props) {
7
7
  <Tableau
8
8
  data={{
9
9
  ...value,
10
- with_sources: true,
11
10
  with_download: true,
12
- with_share: true,
13
11
  }}
14
- sources={[]}
15
12
  breakpoints={
16
13
  config.blocks.blocksConfig.embed_tableau_visualization.breakpoints
17
14
  }
package/src/helpers.js CHANGED
@@ -1,3 +1,5 @@
1
+ import React from 'react';
2
+
1
3
  export const loadTableauScript = (callback, version) => {
2
4
  if (!__CLIENT__) return;
3
5
  const source = `https://public.tableau.com/javascripts/api/tableau-${version}.min.js`;
@@ -32,3 +34,25 @@ export const loadTableauScript = (callback, version) => {
32
34
  // https://public.tableau.com/javascripts/api/tableau-2.2.2.min.js
33
35
  // https://public.tableau.com/javascripts/api/tableau-2.1.2.min.js
34
36
  // 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
+ };
@@ -19,7 +19,6 @@
19
19
  .tableau-wrapper {
20
20
  padding: @tableauWrapperPadding;
21
21
  margin: @tableauWrapperMargin;
22
- background-color: @tableauWrapperBackground;
23
22
 
24
23
  .tableau-debug {
25
24
  margin: 0 auto 1rem auto;
@@ -29,10 +28,6 @@
29
28
  }
30
29
  }
31
30
 
32
- .tableau-info {
33
- margin: 0 auto;
34
- }
35
-
36
31
  .tableau {
37
32
  margin-bottom: 1rem;
38
33
 
@@ -118,25 +113,47 @@
118
113
 
119
114
  // ========= Sources ==========
120
115
 
121
- .tableau-wrapper .tableau-info {
116
+ .tableau-wrapper .visualization-info-container {
122
117
  display: flex;
118
+ flex-wrap: wrap;
123
119
  align-items: center;
124
-
125
- > * {
126
- padding: 0 0.5rem;
127
- border-collapse: collapse;
128
- }
120
+ justify-content: space-between;
121
+ gap: 1.2rem;
129
122
 
130
123
  > *:first-child {
131
- padding-left: 0;
124
+ margin-left: auto;
125
+
126
+ > *:not(:last-child) {
127
+ border-right: 1px solid @textColor;
128
+ }
132
129
  }
133
130
 
134
131
  > *:last-child {
135
- padding-right: 0;
132
+ justify-content: flex-end;
136
133
  }
137
134
 
138
- > *:not(:last-child) {
139
- border-right: 2px solid @textColor;
135
+ .visualization-info {
136
+ display: flex;
137
+ flex-grow: 0.5;
138
+ align-items: center;
139
+
140
+ > * {
141
+ padding: 0 0.5rem;
142
+ border-collapse: collapse;
143
+ }
144
+
145
+ > *:first-child {
146
+ padding-left: 0;
147
+ }
148
+
149
+ > *:last-child {
150
+ padding-right: 0;
151
+ margin-right: 0;
152
+
153
+ &.tableau-download-container {
154
+ margin-left: auto;
155
+ }
156
+ }
140
157
  }
141
158
  }
142
159
 
@@ -144,9 +161,52 @@
144
161
  cursor: pointer;
145
162
  }
146
163
 
164
+ #tableau-share-popup {
165
+ .ui.popup {
166
+ padding-right: 15px;
167
+ padding-left: 0;
168
+ }
169
+
170
+ .tableau-share-popup-text {
171
+ margin-left: 7px;
172
+ }
173
+ }
174
+
175
+ .tableau-share-popup-container {
176
+ display: flex;
177
+ justify-content: space-between;
178
+ gap: 1rem;
179
+
180
+ .tableau-share-link {
181
+ input {
182
+ padding-left: 7px !important;
183
+ border-left: 0 !important;
184
+
185
+ &:focus {
186
+ outline: 1px solid var(--focus-visible, #0083e0);
187
+ }
188
+ }
189
+ }
190
+
191
+ .tableau-copy-button {
192
+ max-width: 80px;
193
+ padding: 10px 17px !important;
194
+ border: 1px solid black !important;
195
+ margin: 0 !important;
196
+ background-color: #4472c4 !important;
197
+ border-radius: 7px !important;
198
+ }
199
+
200
+ @media screen and (max-width: @largestMobileScreen) {
201
+ flex-direction: column;
202
+ }
203
+ }
204
+
205
+ .tableau-note-button,
206
+ .tableau-more-info-button,
147
207
  .tableau-download-button,
148
- .tableau-share-button,
149
- .tableau-sources-button {
208
+ .tableau-sources-button,
209
+ .tableau-share-button {
150
210
  display: inline-flex;
151
211
  align-items: center;
152
212
  padding-bottom: 3px;
@@ -154,7 +214,6 @@
154
214
  background-color: transparent;
155
215
  color: @textColor;
156
216
  cursor: pointer;
157
- font-weight: bold;
158
217
 
159
218
  .icon {
160
219
  margin-left: 0.5rem;
@@ -171,24 +230,25 @@
171
230
  }
172
231
  }
173
232
 
233
+ .tableau-more-info-button,
234
+ .tableau-download-button,
235
+ .tableau-share-button {
236
+ gap: 0.25rem;
237
+
238
+ i {
239
+ margin-bottom: 1px;
240
+ }
241
+ }
242
+
243
+ #tableau-note-popup,
174
244
  #tableau-sources-popup,
175
245
  #tableau-download-popup {
176
246
  .ui.popup {
177
- min-width: 600px;
178
- background-color: #f9f9f9;
247
+ background-color: @grey-1;
179
248
  box-shadow: rgba(0, 0, 0, 0.15) 1.95px 1.95px 2.6px;
180
249
 
181
250
  &::before {
182
- background-color: #f9f9f9;
183
- }
184
-
185
- .sources-list > li,
186
- .sources-list > li > * {
187
- color: @textColor;
188
- }
189
-
190
- a:hover {
191
- color: @secondaryColor;
251
+ background-color: @grey-1;
192
252
  }
193
253
  }
194
254
 
@@ -198,24 +258,72 @@
198
258
  padding-inline-start: 0;
199
259
  }
200
260
 
201
- @media screen and (max-width: @largestMobileScreen) {
261
+ .embed-sources-param-description {
262
+ margin-left: 5px;
263
+ }
264
+
265
+ @media screen and (min-width: @largestMobileScreen) {
202
266
  .ui.popup {
203
- min-width: 300px;
267
+ max-width: 600px;
204
268
  }
205
269
  }
206
270
  }
207
271
 
208
272
  #tableau-download-popup {
209
- .ui.popup {
210
- min-width: 460px;
211
- text-align: center;
273
+ ul.no-bullets {
274
+ padding: 0;
275
+ margin: 0;
276
+ list-style-type: none;
277
+ }
278
+
279
+ .visualization-wrapper {
280
+ position: relative;
281
+
282
+ .visualization-info {
283
+ display: flex;
284
+ align-items: center;
285
+
286
+ > * {
287
+ padding: 0 0.5rem;
288
+ border-collapse: collapse;
289
+ }
290
+
291
+ > *:first-child {
292
+ padding-left: 0;
293
+ }
294
+
295
+ > *:last-child {
296
+ padding-right: 0;
297
+ margin-right: 0;
298
+
299
+ &.tableau-download-container {
300
+ margin-left: auto;
301
+ }
302
+ }
303
+
304
+ > *:not(:last-child) {
305
+ border-right: 2px solid @textColor;
306
+ }
307
+ }
308
+ }
309
+
310
+ .tableau-download-button.tableau-format-download {
311
+ padding: 0;
312
+ color: #679ad6;
313
+ font-weight: normal;
314
+
315
+ &:hover {
316
+ color: #164b7f;
317
+ }
212
318
  }
213
319
  }
214
320
 
215
321
  @media print {
216
322
  .tableau-download-container,
323
+ .tableau-note-container,
217
324
  .tableau-sources-container,
218
325
  .tableau-share-container,
326
+ #tableau-note-popup,
219
327
  #tableau-sources-popup {
220
328
  display: none;
221
329
  }
@@ -5,4 +5,6 @@
5
5
  @tableauIframePadding : 0;
6
6
  @tableauIframeMargin : 0 auto 1rem auto;
7
7
  @tableauIframeBorder : none;
8
- @tableauIframeBorderRadius : 0;
8
+ @tableauIframeBorderRadius : 0;
9
+
10
+ @grey-1 : #f9f9f9;