@eeacms/volto-tableau 2.0.0 → 3.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.
@@ -0,0 +1,47 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const projectRootPath = fs.existsSync('./project')
5
+ ? fs.realpathSync('./project')
6
+ : fs.realpathSync('./../../../');
7
+ const packageJson = require(path.join(projectRootPath, 'package.json'));
8
+ const jsConfig = require(path.join(projectRootPath, 'jsconfig.json'))
9
+ .compilerOptions;
10
+
11
+ const pathsConfig = jsConfig.paths;
12
+
13
+ let voltoPath = path.join(projectRootPath, 'node_modules/@plone/volto');
14
+
15
+ Object.keys(pathsConfig).forEach((pkg) => {
16
+ if (pkg === '@plone/volto') {
17
+ voltoPath = `./${jsConfig.baseUrl}/${pathsConfig[pkg][0]}`;
18
+ }
19
+ });
20
+ const AddonConfigurationRegistry = require(`${voltoPath}/addon-registry.js`);
21
+ const reg = new AddonConfigurationRegistry(projectRootPath);
22
+
23
+ // Extends ESlint configuration for adding the aliases to `src` directories in Volto addons
24
+ const addonAliases = Object.keys(reg.packages).map((o) => [
25
+ o,
26
+ reg.packages[o].modulePath,
27
+ ]);
28
+
29
+ module.exports = {
30
+ extends: `${projectRootPath}/node_modules/@plone/volto/.eslintrc`,
31
+ settings: {
32
+ 'import/resolver': {
33
+ alias: {
34
+ map: [
35
+ ['@plone/volto', '@plone/volto/src'],
36
+ ...addonAliases,
37
+ ['@package', `${__dirname}/src`],
38
+ ['~', `${__dirname}/src`],
39
+ ],
40
+ extensions: ['.js', '.jsx', '.json'],
41
+ },
42
+ 'babel-plugin-root-import': {
43
+ rootPathSuffix: 'src',
44
+ },
45
+ },
46
+ },
47
+ };
package/CHANGELOG.md CHANGED
@@ -4,7 +4,28 @@ 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
- ### [2.0.0](https://github.com/eea/volto-tableau/compare/1.3.0...2.0.0) - 31 October 2022
7
+ ### [3.0.0](https://github.com/eea/volto-tableau/compare/2.0.0...3.0.0) - 12 January 2023
8
+
9
+ #### :hammer_and_wrench: Others
10
+
11
+ - Remove sources from schema [dana-cfc4 - [`0d2b862`](https://github.com/eea/volto-tableau/commit/0d2b86212b72e06d4ab81804395eda0c2919aeb8)]
12
+ - setup the loader, script loading, data provenance, error catching [andreiggr - [`bc65898`](https://github.com/eea/volto-tableau/commit/bc65898edf878f8d011573f91fb72fb1a956d8e9)]
13
+ - merge with better_tableau_import [andreiggr - [`2d8f314`](https://github.com/eea/volto-tableau/commit/2d8f314a939f1122c0307ccfebf4215bcea2c148)]
14
+ - better loader response [andreiggr - [`d6f3c86`](https://github.com/eea/volto-tableau/commit/d6f3c86cc97475819db71a99108e4da739d66576)]
15
+ - Add ConnectedTableau [dana-cfc4 - [`d83417d`](https://github.com/eea/volto-tableau/commit/d83417d1b9ea454ded0b6f24e581ac2a85c32780)]
16
+ - lint [andreiggr - [`f268b10`](https://github.com/eea/volto-tableau/commit/f268b10710a77cd19d9cc110d60447d495a747ed)]
17
+ - fix crashing sources && better getContent [andreiggr - [`a202967`](https://github.com/eea/volto-tableau/commit/a202967340bcb8e366868193d236d9f49b563bfc)]
18
+ - somewhat better import [andreiggr - [`48219a3`](https://github.com/eea/volto-tableau/commit/48219a368b7165ef9e9df3cf65e643be72f5f8b0)]
19
+ - Fix style lint errors [dana-cfc4 - [`ed1873c`](https://github.com/eea/volto-tableau/commit/ed1873ce27c81e6f2a2459f38bc4cd10d65b559b)]
20
+ - Fix initValue not initialized [dana-cfc4 - [`bbe5802`](https://github.com/eea/volto-tableau/commit/bbe5802c8e2ee794b15570959a00307531b32d96)]
21
+ - Add placeholder in case of errors [dana-cfc4 - [`1af2d0d`](https://github.com/eea/volto-tableau/commit/1af2d0da04635716f4aef320c5f84694a59435e7)]
22
+ - importmap for versions of tableau [Andrei Grigore - [`4e03cb2`](https://github.com/eea/volto-tableau/commit/4e03cb2c3a3bf11d186462067d12c2685b9bf607)]
23
+ - Increase addon version [danac - [`26b58c5`](https://github.com/eea/volto-tableau/commit/26b58c57b6cb4be4eed23fe3343c65250e6d593a)]
24
+ - Add data provenance [danac - [`bd46995`](https://github.com/eea/volto-tableau/commit/bd4699547be72b2d9dad292ee3681b937e741239)]
25
+ - Add eslint config [danac - [`394e5d1`](https://github.com/eea/volto-tableau/commit/394e5d11591610a97cb5b57d882da818b0128b7e)]
26
+ - Fix formatting issues [danac - [`2ef1bae`](https://github.com/eea/volto-tableau/commit/2ef1bae5caf44fb144f8251aed2895df2e03de7d)]
27
+ - Add Embed EEA Tableau block [danac - [`54e45b2`](https://github.com/eea/volto-tableau/commit/54e45b2b0b29d01173315ffe47e74938e43cdf23)]
28
+ ## [2.0.0](https://github.com/eea/volto-tableau/compare/1.3.0...2.0.0) - 1 November 2022
8
29
 
9
30
  #### :bug: Bug Fixes
10
31
 
@@ -71,7 +71,8 @@ Cypress.Commands.add(
71
71
  id: contentId,
72
72
  title: contentTitle,
73
73
  image: {
74
- data: 'iVBORw0KGgoAAAANSUhEUgAAANcAAAA4CAMAAABZsZ3QAAAAM1BMVEX29fK42OU+oMvn7u9drtIPisHI4OhstdWZyt4fkcXX5+sAg74umMhNp86p0eJ7vNiKw9v/UV4wAAAAAXRSTlMAQObYZgAABBxJREFUeF7tmuty4yAMhZG4X2zn/Z92J5tsBJwWXG/i3XR6frW2Y/SBLIRAfaQUDNt8E5tLUt9BycfcKfq3R6Mlfyimtx4rzp+K3dtibXkor99zsEqLYZltblTecciogoh+TXfY1Ve4dn07rCDGG9dHSEEOg/GmXl0U1XDxTKxNK5De7BxsyyBr6gGm2/vPxKJ8F6f7BXKfRMp1xIWK9A+5ks25alSb353dWnDJN1k35EL5f8dVGifTf/4tjUuuFq7u4srmXC60yAmldLXIWbg65RKU87lcGxJCFqUPv0IacW0PmSivOZFLE908inPToMmii/roG+MRV/O8FU88i8tFsxV3a06MFUw0Qu7RmAtdV5/HVVaOVMTWNOWSwMljLhzhcB6XIS7OK5V6AvRDNN7t5VJWQs1J40UmalbK56usBG/CuCHSYuc+rkUGeMCViNRARPrzW52N3oQLe6WifNliSuuGaH3czbVNudI9s7ZLUCLHVwWlyES522o1t14uvmbblmVTKqFjaZYJFSTPP4dLL1kU1z7p0lzdbRulmEWLxoQX+z9ce7A8GqEEucllLxePuZwdJl1Lezu0hoswvTPt61DrFcRuujV/2cmlxaGBC7Aw6cpovGANwRiSdOAWJ5AGy4gLL64dl0QhUEAuEUNws+XxV+OKGPdw/hESGYF9XEGaFC7sNLMSXWJjHsnanYi87VK428N2uxpOjOFANcagLM5l+7mSycM8KknZpKLcGi6jmzWGr/vLurZ/0g4u9AZuAoeb5r1ceQhyiTPY1E4wUR6u/F3H2ojSpXMMriBPT9cezTto8Cx+MsglHL4fv1Rxrb1LVw9yvyQpJ3AhFnLZfuRLH2QsOG3FGGD20X/th/u5bFAt16Bt308KjF+MNOXgl/SquIEySX3GhaZvc67KZbDxcCDORz2N8yCWPaY5lyQZO7lQ29fnZbt3Xu6qoge4+DjXl/MocySPOp9rlvdyznahRyHEYd77v3LhugOXDv4J65QXfl803BDAdaWBEDhfVx7nKofjoVCgxnUAqw/UAUDPn788BDvQuG4TDtdtUPvzjSlXAB8DvaDOhhrmhwbywylXAm8CvaouikJTL93gs3y7Yy4VYbIxOHrcMizPqWOjqO9l3Uz52kibQy4xxOgqhJvD+w5rvokOcAlGvNCfeqCv1ste1stzLm0f71Iq3ZfTrPfuE5nhPtF+LvQE2lffQC7pYtQy3tdzdrKvd5TLVVzDetScS3nEKmmwDyt1Cev1kX3YfbvzNK4fzrlw+cB6vm+uiUgf2zdXI62241LawCb7Pi5FXFPF8KpzDoF/Sw2lg+GrHNbno1mhPu+VCF/vfMnw06PnUl6j48dVHD3jHNHPua+fc3o/5yp/zsGi0vYtzi3Pz5mHd4T6BWMIlewacd63AAAAAElFTkSuQmCC',
74
+ data:
75
+ 'iVBORw0KGgoAAAANSUhEUgAAANcAAAA4CAMAAABZsZ3QAAAAM1BMVEX29fK42OU+oMvn7u9drtIPisHI4OhstdWZyt4fkcXX5+sAg74umMhNp86p0eJ7vNiKw9v/UV4wAAAAAXRSTlMAQObYZgAABBxJREFUeF7tmuty4yAMhZG4X2zn/Z92J5tsBJwWXG/i3XR6frW2Y/SBLIRAfaQUDNt8E5tLUt9BycfcKfq3R6Mlfyimtx4rzp+K3dtibXkor99zsEqLYZltblTecciogoh+TXfY1Ve4dn07rCDGG9dHSEEOg/GmXl0U1XDxTKxNK5De7BxsyyBr6gGm2/vPxKJ8F6f7BXKfRMp1xIWK9A+5ks25alSb353dWnDJN1k35EL5f8dVGifTf/4tjUuuFq7u4srmXC60yAmldLXIWbg65RKU87lcGxJCFqUPv0IacW0PmSivOZFLE908inPToMmii/roG+MRV/O8FU88i8tFsxV3a06MFUw0Qu7RmAtdV5/HVVaOVMTWNOWSwMljLhzhcB6XIS7OK5V6AvRDNN7t5VJWQs1J40UmalbK56usBG/CuCHSYuc+rkUGeMCViNRARPrzW52N3oQLe6WifNliSuuGaH3czbVNudI9s7ZLUCLHVwWlyES522o1t14uvmbblmVTKqFjaZYJFSTPP4dLL1kU1z7p0lzdbRulmEWLxoQX+z9ce7A8GqEEucllLxePuZwdJl1Lezu0hoswvTPt61DrFcRuujV/2cmlxaGBC7Aw6cpovGANwRiSdOAWJ5AGy4gLL64dl0QhUEAuEUNws+XxV+OKGPdw/hESGYF9XEGaFC7sNLMSXWJjHsnanYi87VK428N2uxpOjOFANcagLM5l+7mSycM8KknZpKLcGi6jmzWGr/vLurZ/0g4u9AZuAoeb5r1ceQhyiTPY1E4wUR6u/F3H2ojSpXMMriBPT9cezTto8Cx+MsglHL4fv1Rxrb1LVw9yvyQpJ3AhFnLZfuRLH2QsOG3FGGD20X/th/u5bFAt16Bt308KjF+MNOXgl/SquIEySX3GhaZvc67KZbDxcCDORz2N8yCWPaY5lyQZO7lQ29fnZbt3Xu6qoge4+DjXl/MocySPOp9rlvdyznahRyHEYd77v3LhugOXDv4J65QXfl803BDAdaWBEDhfVx7nKofjoVCgxnUAqw/UAUDPn788BDvQuG4TDtdtUPvzjSlXAB8DvaDOhhrmhwbywylXAm8CvaouikJTL93gs3y7Yy4VYbIxOHrcMizPqWOjqO9l3Uz52kibQy4xxOgqhJvD+w5rvokOcAlGvNCfeqCv1ste1stzLm0f71Iq3ZfTrPfuE5nhPtF+LvQE2lffQC7pYtQy3tdzdrKvd5TLVVzDetScS3nEKmmwDyt1Cev1kX3YfbvzNK4fzrlw+cB6vm+uiUgf2zdXI62241LawCb7Pi5FXFPF8KpzDoF/Sw2lg+GrHNbno1mhPu+VCF/vfMnw06PnUl6j48dVHD3jHNHPua+fc3o/5yp/zsGi0vYtzi3Pz5mHd4T6BWMIlewacd63AAAAAElFTkSuQmCC',
75
76
  encoding: 'base64',
76
77
  filename: 'image.png',
77
78
  'content-type': 'image/png',
@@ -124,7 +125,7 @@ Cypress.Commands.add(
124
125
  })
125
126
  .then(() => console.log(`${contentType} created`));
126
127
  }
127
- }
128
+ },
128
129
  );
129
130
 
130
131
  // --- Add DX Content-Type ----------------------------------------------------------
@@ -217,7 +218,7 @@ Cypress.Commands.add('removeSlateJSONField', (type, name) => {
217
218
  body: {},
218
219
  })
219
220
  .then(() =>
220
- console.log(`${name} SlateJSONField field removed from ${type}`)
221
+ console.log(`${name} SlateJSONField field removed from ${type}`),
221
222
  );
222
223
  });
223
224
 
@@ -251,7 +252,7 @@ Cypress.Commands.add('typeInSlate', { prevSubject: true }, (subject, text) => {
251
252
  new InputEvent('beforeinput', {
252
253
  inputType: 'insertText',
253
254
  data: text,
254
- })
255
+ }),
255
256
  );
256
257
  return subject;
257
258
  })
@@ -267,7 +268,7 @@ Cypress.Commands.add('lineBreakInSlate', { prevSubject: true }, (subject) => {
267
268
  .wrap(subject)
268
269
  .then((subject) => {
269
270
  subject[0].dispatchEvent(
270
- new InputEvent('beforeinput', { inputType: 'insertLineBreak' })
271
+ new InputEvent('beforeinput', { inputType: 'insertLineBreak' }),
271
272
  );
272
273
  return subject;
273
274
  })
@@ -315,7 +316,7 @@ Cypress.Commands.add(
315
316
  include_children: include_children,
316
317
  },
317
318
  });
318
- }
319
+ },
319
320
  );
320
321
 
321
322
  // --- waitForResourceToLoad ----------------------------------------------------------
@@ -375,7 +376,7 @@ Cypress.Commands.add(
375
376
  setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
376
377
  }
377
378
  });
378
- }
379
+ },
379
380
  );
380
381
 
381
382
  Cypress.Commands.add('getSlate', ({ createNewSlate = true } = {}) => {
@@ -390,7 +391,7 @@ Cypress.Commands.add('getSlate', ({ createNewSlate = true } = {}) => {
390
391
  cy.get('.block.inner').last().type('{moveToEnd}{enter}');
391
392
  }
392
393
  slate = cy.get(SLATE_SELECTOR, { timeout: 10000 }).last();
393
- }
394
+ },
394
395
  );
395
396
  return slate;
396
397
  });
@@ -470,7 +471,7 @@ Cypress.Commands.add(
470
471
  });
471
472
  // Depending on what you're testing, you may need to chain a `.click()` here to ensure
472
473
  // further commands are picked up by whatever you're testing (this was required for Slate, for example).
473
- }
474
+ },
474
475
  );
475
476
 
476
477
  Cypress.Commands.add(
@@ -478,7 +479,7 @@ Cypress.Commands.add(
478
479
  { prevSubject: true },
479
480
  (subject, query) => {
480
481
  cy.wrap(subject).setCursor(query, true);
481
- }
482
+ },
482
483
  );
483
484
 
484
485
  Cypress.Commands.add(
@@ -486,7 +487,7 @@ Cypress.Commands.add(
486
487
  { prevSubject: true },
487
488
  (subject, query) => {
488
489
  cy.wrap(subject).setCursor(query);
489
- }
490
+ },
490
491
  );
491
492
 
492
493
  // Helper functions
@@ -532,5 +533,5 @@ Cypress.Commands.add(
532
533
  failAction();
533
534
  }
534
535
  });
535
- }
536
+ },
536
537
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-tableau",
3
- "version": "2.0.0",
3
+ "version": "3.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",
@@ -0,0 +1,55 @@
1
+ import React from 'react';
2
+ import BlockDataForm from '@plone/volto/components/manage/Form/BlockDataForm';
3
+ import { SidebarPortal } from '@plone/volto/components';
4
+ import { getContent } from '@plone/volto/actions';
5
+ import View from './View';
6
+ import Schema from './schema';
7
+ import { connect } from 'react-redux';
8
+ import { compose } from 'redux';
9
+
10
+ const Edit = (props) => {
11
+ const { data, block, onChangeBlock, id } = props;
12
+ const schema = React.useMemo(() => Schema(props), [props]);
13
+
14
+ React.useEffect(() => {
15
+ if (!Object.hasOwn(data, 'show_sources')) {
16
+ onChangeBlock(block, {
17
+ ...data,
18
+ show_sources: true,
19
+ });
20
+ }
21
+ }, [block, data, onChangeBlock]);
22
+
23
+ return (
24
+ <>
25
+ <View data={data} id={id} />
26
+ <SidebarPortal selected={props.selected}>
27
+ <BlockDataForm
28
+ block={block}
29
+ title={schema.title}
30
+ schema={schema}
31
+ onChangeField={(id, value) => {
32
+ props.onChangeBlock(block, {
33
+ ...data,
34
+ [id]: value,
35
+ });
36
+ }}
37
+ formData={data}
38
+ />
39
+ </SidebarPortal>
40
+ </>
41
+ );
42
+ };
43
+
44
+ export default compose(
45
+ connect(
46
+ (state, props) => ({
47
+ block_data: state.content.data,
48
+ data_provenance:
49
+ state.content.subrequests?.[props.id]?.data?.data_provenance,
50
+ }),
51
+ {
52
+ getContent,
53
+ },
54
+ ),
55
+ )(Edit);
@@ -0,0 +1,55 @@
1
+ import React from 'react';
2
+ import ConnectedTableau from '../../ConnectedTableau/ConnectedTableau';
3
+
4
+ import { getContent } from '@plone/volto/actions';
5
+
6
+ import { connect } from 'react-redux';
7
+ import { compose } from 'redux';
8
+
9
+ const View = (props) => {
10
+ const { data } = props || {};
11
+ const { vis_url = '' } = data;
12
+ const show_sources = data?.show_sources ?? false;
13
+
14
+ React.useEffect(() => {
15
+ if (vis_url) {
16
+ props.getContent(vis_url, null, props.id);
17
+ }
18
+ // eslint-disable-next-line react-hooks/exhaustive-deps
19
+ }, [vis_url]);
20
+
21
+ return (
22
+ <>
23
+ {data?.vis_url ? (
24
+ <>
25
+ <ConnectedTableau {...props.tableau_visualization} id={props.id} />
26
+ {show_sources &&
27
+ data.tableauSources &&
28
+ props.tableau_visualization ? (
29
+ ''
30
+ ) : show_sources ? (
31
+ <div>Data provenance is not set in the visualization</div>
32
+ ) : (
33
+ ''
34
+ )}
35
+ </>
36
+ ) : (
37
+ <div>Please select a visualization from block editor.</div>
38
+ )}
39
+ </>
40
+ );
41
+ };
42
+
43
+ export default compose(
44
+ connect(
45
+ (state, props) => ({
46
+ data_provenance:
47
+ state.content.subrequests?.[props.id]?.data?.data_provenance,
48
+ tableau_visualization:
49
+ state.content.subrequests?.[props.id]?.data?.tableau_visualization_data,
50
+ }),
51
+ {
52
+ getContent,
53
+ },
54
+ ),
55
+ )(View);
@@ -0,0 +1,31 @@
1
+ const Schema = (props) => {
2
+ return {
3
+ title: 'Embed EEA Tableau',
4
+ fieldsets: [
5
+ {
6
+ id: 'default',
7
+ title: 'Default',
8
+ fields: ['vis_url', 'height', 'show_sources'],
9
+ },
10
+ ],
11
+ properties: {
12
+ vis_url: {
13
+ widget: 'object_by_path',
14
+ title: 'Visualization',
15
+ },
16
+ height: {
17
+ title: 'Height',
18
+ type: 'number',
19
+ default: 450,
20
+ },
21
+ show_sources: {
22
+ title: 'Toggle sources',
23
+ type: 'boolean',
24
+ },
25
+ },
26
+
27
+ required: ['vis_url'],
28
+ };
29
+ };
30
+
31
+ export default Schema;
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import Tableau from '@eeacms/volto-tableau/Tableau/View';
3
+
4
+ const ConnectedTableau = (props) => {
5
+ const [error, setError] = React.useState(null);
6
+ const [loaded, setLoaded] = React.useState(null);
7
+ return (
8
+ <div className="tableau-block">
9
+ <Tableau
10
+ error={error}
11
+ loaded={loaded}
12
+ setError={setError}
13
+ setLoaded={setLoaded}
14
+ data={{ ...props?.general, ...props?.options, ...props?.extraOptions }}
15
+ url={props?.general?.url}
16
+ />
17
+ </div>
18
+ );
19
+ };
20
+
21
+ export default ConnectedTableau;
@@ -5,7 +5,7 @@ import { toast } from 'react-toastify';
5
5
  import { Toast } from '@plone/volto/components';
6
6
  import { setTableauApi } from '@eeacms/volto-tableau/actions';
7
7
  import cx from 'classnames';
8
- import { isMyScriptLoaded, loadTableauScript } from '../helpers';
8
+ import { loadTableauScript } from '../helpers';
9
9
 
10
10
  const Tableau = (props) => {
11
11
  const ref = React.useRef(null);
@@ -36,7 +36,7 @@ const Tableau = (props) => {
36
36
  const url = props.url || defaultUrl;
37
37
 
38
38
  //load tableau from script tag
39
- const tableau = isMyScriptLoaded(version) && __CLIENT__ ? window.tableau : '';
39
+ const tableau = loadTableauScript(() => {}, version);
40
40
 
41
41
  const onFilterChange = (filter) => {
42
42
  const newFilters = { ...filters.current };
@@ -186,6 +186,7 @@ const Tableau = (props) => {
186
186
  tableau,
187
187
  toolbarPosition,
188
188
  url,
189
+ version,
189
190
  ]);
190
191
 
191
192
  React.useEffect(() => {
@@ -205,6 +206,19 @@ const Tableau = (props) => {
205
206
  return (
206
207
  <div id="tableau-wrap">
207
208
  <div id="tableau-outer">
209
+ {data && Object.keys(data).length > 0 ? (
210
+ <>
211
+ {loaded ? (
212
+ ''
213
+ ) : (
214
+ <div className="tableau-loader">
215
+ <span>Loading Tableau v{version}</span>
216
+ </div>
217
+ )}
218
+ </>
219
+ ) : (
220
+ <div>No data present in that visualization.</div>
221
+ )}
208
222
  <div
209
223
  className={cx('tableau', version, {
210
224
  'tableau-scale': autoScale,
@@ -47,6 +47,11 @@ const View = (props) => {
47
47
  /* eslint-disable-next-line */
48
48
  }, []);
49
49
 
50
+ React.useEffect(() => {
51
+ if (props.setTableauError) props.setTableauError(error);
52
+ /* eslint-disable-next-line */
53
+ }, [error]);
54
+
50
55
  React.useEffect(() => {
51
56
  const newExtraFilters = { ...extraFilters };
52
57
  urlParameters.forEach((element) => {
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+ import TableauView from '../TableauBlock/View';
3
+
4
+ const VisualizationView = (props) => {
5
+ const [tableauError, setTableauError] = React.useState('');
6
+ const { content = {} } = props;
7
+ const { tableau_visualization_data = {} } = content;
8
+
9
+ const TableauNotDisplayed = () => {
10
+ return (
11
+ <div className="tableau-block not_displayed_tableau">
12
+ <div className="tableau-info">
13
+ {!tableau_visualization_data.general?.url ? (
14
+ <p className="tableau-error">URL required</p>
15
+ ) : tableauError ? (
16
+ <p className="tableau-error">{tableauError}</p>
17
+ ) : (
18
+ ''
19
+ )}
20
+ </div>
21
+ </div>
22
+ );
23
+ };
24
+ return (
25
+ <div>
26
+ {!tableau_visualization_data.general?.url || tableauError ? (
27
+ <TableauNotDisplayed />
28
+ ) : (
29
+ ''
30
+ )}
31
+ <TableauView
32
+ setTableauError={setTableauError}
33
+ data={{
34
+ ...tableau_visualization_data.general,
35
+ ...tableau_visualization_data.options,
36
+ ...tableau_visualization_data.extraOptions,
37
+ }}
38
+ />
39
+ </div>
40
+ );
41
+ };
42
+
43
+ export default VisualizationView;
@@ -0,0 +1,3 @@
1
+ import VisualizationView from './VisualizationView';
2
+
3
+ export { VisualizationView };
@@ -0,0 +1,158 @@
1
+ import React from 'react';
2
+ import { Modal, Button, Grid } from 'semantic-ui-react';
3
+ import '@eeacms/volto-tableau/less/tableau.less';
4
+ import config from '@plone/volto/registry';
5
+
6
+ import { FormFieldWrapper, InlineForm } from '@plone/volto/components';
7
+
8
+ import TableauView from '../TableauBlock/View';
9
+ import Schema from './schema';
10
+
11
+ const VisualizationWidget = (props) => {
12
+ const [open, setOpen] = React.useState(false);
13
+ const { onChange = {}, id } = props;
14
+
15
+ const block = React.useMemo(() => props.block, [props.block]);
16
+ const value = React.useMemo(() => props.value, [props.value]);
17
+
18
+ const [intValue, setIntValue] = React.useState(value);
19
+ const [tableauError, setTableauError] = React.useState('');
20
+
21
+ const dataForm = { tableau_data: intValue };
22
+
23
+ const handleApplyChanges = () => {
24
+ onChange(id, intValue);
25
+ setOpen(false);
26
+ };
27
+
28
+ const handleClose = () => {
29
+ setIntValue(value);
30
+ setOpen(false);
31
+ };
32
+
33
+ const handleChangeField = (val) => {
34
+ setIntValue(val);
35
+ };
36
+
37
+ const TableauNotDisplayed = () => {
38
+ return (
39
+ <div className="tableau-block not_displayed_tableau">
40
+ <div className="tableau-info">
41
+ {intValue && intValue.general && !intValue.general.url ? (
42
+ <p className="tableau-error">URL required</p>
43
+ ) : tableauError ? (
44
+ <p className="tableau-error">{tableauError}</p>
45
+ ) : (
46
+ ''
47
+ )}
48
+ </div>
49
+ </div>
50
+ );
51
+ };
52
+
53
+ let schema = Schema(config);
54
+
55
+ React.useEffect(() => {
56
+ if (!intValue?.options) {
57
+ setIntValue({
58
+ ...intValue,
59
+ options: {
60
+ autoScale: false,
61
+ hideTabs: false,
62
+ hideToolbar: false,
63
+ toolbarPosition: 'Top',
64
+ },
65
+ });
66
+ }
67
+ // eslint-disable-next-line react-hooks/exhaustive-deps
68
+ }, [intValue]);
69
+
70
+ return (
71
+ <FormFieldWrapper {...props}>
72
+ <div className="wrapper">
73
+ <Button
74
+ floated="right"
75
+ size="tiny"
76
+ onClick={(e) => {
77
+ e.preventDefault();
78
+ e.stopPropagation();
79
+ setOpen(true);
80
+ }}
81
+ >
82
+ Open Tableau Editor
83
+ </Button>
84
+ </div>
85
+
86
+ {open && (
87
+ <Modal
88
+ id="tableau-editor-modal"
89
+ style={{ width: '95% !important' }}
90
+ open={true}
91
+ >
92
+ <Modal.Content scrolling>
93
+ <Grid stackable reversed="mobile vertically tablet vertically">
94
+ <Grid.Column
95
+ mobile={12}
96
+ tablet={12}
97
+ computer={5}
98
+ className="tableau-editor-column"
99
+ >
100
+ <InlineForm
101
+ block={block}
102
+ schema={schema}
103
+ onChangeField={(id, value) => {
104
+ handleChangeField(value);
105
+ }}
106
+ formData={dataForm}
107
+ />
108
+ </Grid.Column>
109
+ <Grid.Column mobile={12} tablet={12} computer={7}>
110
+ {(intValue && intValue.general && !intValue.general.url) ||
111
+ tableauError ? (
112
+ <TableauNotDisplayed />
113
+ ) : (
114
+ ''
115
+ )}
116
+ <div className="tableau-container">
117
+ <TableauView
118
+ setTableauError={setTableauError}
119
+ data={{
120
+ ...intValue?.general,
121
+ ...intValue?.options,
122
+ ...intValue?.extraOptions,
123
+ }}
124
+ />
125
+ </div>
126
+ </Grid.Column>
127
+ </Grid>
128
+ </Modal.Content>
129
+ <Modal.Actions>
130
+ <Grid>
131
+ <Grid.Row>
132
+ <div className="map-edit-actions-container">
133
+ <Button onClick={handleClose}>Close</Button>
134
+ <Button color="green" onClick={handleApplyChanges}>
135
+ Apply changes
136
+ </Button>
137
+ </div>
138
+ </Grid.Row>
139
+ </Grid>
140
+ </Modal.Actions>
141
+ </Modal>
142
+ )}
143
+ {(intValue && intValue.general && !intValue.general.url) ||
144
+ tableauError ? (
145
+ <TableauNotDisplayed />
146
+ ) : (
147
+ ''
148
+ )}
149
+
150
+ <TableauView
151
+ setTableauError={setTableauError}
152
+ data={{ ...value?.general, ...value?.options, ...value?.extraOptions }}
153
+ />
154
+ </FormFieldWrapper>
155
+ );
156
+ };
157
+
158
+ export default VisualizationWidget;
@@ -0,0 +1,3 @@
1
+ import VisualizationWidget from './VisualizationWidget';
2
+
3
+ export { VisualizationWidget };
@@ -0,0 +1,176 @@
1
+ const generalSchema = {
2
+ title: 'General',
3
+
4
+ fieldsets: [
5
+ {
6
+ id: 'general',
7
+ title: 'General',
8
+ fields: ['url'],
9
+ },
10
+ ],
11
+
12
+ properties: {
13
+ url: {
14
+ title: 'Url',
15
+ type: 'textarea',
16
+ },
17
+ },
18
+ required: ['url'],
19
+ };
20
+
21
+ const optionsSchema = {
22
+ title: 'Options',
23
+
24
+ fieldsets: [
25
+ {
26
+ id: 'options',
27
+ title: 'Options',
28
+ fields: [
29
+ 'sheetname',
30
+ 'hideTabs',
31
+ 'hideToolbar',
32
+ 'autoScale',
33
+ 'toolbarPosition',
34
+ ],
35
+ },
36
+ ],
37
+
38
+ properties: {
39
+ sheetname: {
40
+ title: 'Sheetname',
41
+ type: 'text',
42
+ },
43
+ hideTabs: {
44
+ title: 'Hide tabs',
45
+ type: 'boolean',
46
+ default: false,
47
+ },
48
+ hideToolbar: {
49
+ title: 'Hide toolbar',
50
+ type: 'boolean',
51
+ default: false,
52
+ },
53
+ autoScale: {
54
+ title: 'Auto scale',
55
+ type: 'boolean',
56
+ default: false,
57
+ description: 'Scale down tableau according to width',
58
+ },
59
+ toolbarPosition: {
60
+ title: 'Toolbar position',
61
+ type: 'array',
62
+ choices: [
63
+ ['Top', 'Top'],
64
+ ['Bottom', 'Bottom'],
65
+ ],
66
+ default: 'Top',
67
+ },
68
+ },
69
+ required: [],
70
+ };
71
+
72
+ const urlParametersSchema = {
73
+ title: 'Parameter',
74
+ fieldsets: [
75
+ { id: 'default', title: 'Default', fields: ['field', 'urlParam'] },
76
+ ],
77
+ properties: {
78
+ field: {
79
+ title: 'Tableau fieldname',
80
+ type: 'text',
81
+ },
82
+ urlParam: {
83
+ title: 'URL param',
84
+ type: 'text',
85
+ },
86
+ },
87
+ required: [],
88
+ };
89
+
90
+ const breakpointUrlSchema = (config) => {
91
+ const breakpoints = config.blocks.blocksConfig.tableau_block.breakpoints;
92
+
93
+ return {
94
+ title: 'URL',
95
+ fieldsets: [{ id: 'default', title: 'Default', fields: ['device', 'url'] }],
96
+ properties: {
97
+ device: {
98
+ title: 'Device',
99
+ type: 'array',
100
+ choices: Object.keys(breakpoints).map((breakpoint) => [
101
+ breakpoint,
102
+ breakpoint,
103
+ ]),
104
+ },
105
+ url: {
106
+ title: 'Url',
107
+ widget: 'textarea',
108
+ },
109
+ },
110
+ required: [],
111
+ };
112
+ };
113
+
114
+ const extraOptionsSchema = (config) => {
115
+ return {
116
+ title: 'Extra Options',
117
+
118
+ fieldsets: [
119
+ {
120
+ id: 'default',
121
+ title: 'Extra Options Data',
122
+ fields: ['urlParameters', 'breakpointUrls'],
123
+ },
124
+ ],
125
+
126
+ properties: {
127
+ urlParameters: {
128
+ title: 'URL parameters',
129
+ widget: 'object_list',
130
+ schema: urlParametersSchema,
131
+ description: 'Set a list of url parameters to filter the tableau',
132
+ },
133
+ breakpointUrls: {
134
+ title: 'Breakpoint urls',
135
+ widget: 'object_list',
136
+ schema: breakpointUrlSchema(config),
137
+ description: 'Set different vizualization for specific breakpoint',
138
+ },
139
+ },
140
+ required: [],
141
+ };
142
+ };
143
+
144
+ export default (config) => {
145
+ return {
146
+ title: 'Tableau Editor',
147
+ fieldsets: [
148
+ {
149
+ id: 'default',
150
+ title: 'Tableau Editor Sections',
151
+ fields: ['tableau_data'],
152
+ },
153
+ ],
154
+ properties: {
155
+ tableau_data: {
156
+ title: 'Panels',
157
+ widget: 'object_types_widget',
158
+ schemas: [
159
+ {
160
+ id: 'general',
161
+ schema: generalSchema,
162
+ },
163
+ {
164
+ id: 'options',
165
+ schema: optionsSchema,
166
+ },
167
+ {
168
+ id: 'extraOptions',
169
+ schema: extraOptionsSchema(config),
170
+ },
171
+ ],
172
+ },
173
+ },
174
+ required: [],
175
+ };
176
+ };
@@ -0,0 +1,8 @@
1
+ .sidebar-container-enter-done {
2
+ z-index: 1001 !important;
3
+ }
4
+
5
+ .field-provider-data-action-button {
6
+ border: 1px solid #c7d5d8;
7
+ cursor: pointer;
8
+ }
package/src/helpers.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const loadTableauScript = (callback, version) => {
2
- const existingScript = document.getElementById(`tableauJS`);
2
+ const existingScript = __CLIENT__ && document.getElementById(`tableauJS`);
3
3
  //replace script loaded on each version change
4
4
  if (existingScript) {
5
5
  existingScript.setAttribute(
@@ -7,7 +7,7 @@ const loadTableauScript = (callback, version) => {
7
7
  `https://public.tableau.com/javascripts/api/tableau-${version}.min.js`,
8
8
  );
9
9
  }
10
- if (!existingScript) {
10
+ if (!existingScript && __CLIENT__) {
11
11
  const script = document.createElement('script');
12
12
  script.src = `https://public.tableau.com/javascripts/api/tableau-${version}.min.js`;
13
13
  script.id = `tableauJS`;
@@ -18,14 +18,21 @@ const loadTableauScript = (callback, version) => {
18
18
  }
19
19
  //callback, if needed
20
20
  if (existingScript && callback) callback();
21
+
22
+ const tableau = isMyScriptLoaded(version) && __CLIENT__ ? window.tableau : '';
23
+ return tableau;
21
24
  };
22
25
 
23
- const isMyScriptLoaded = (id) => {
26
+ const isMyScriptLoaded = (version) => {
24
27
  //check for loaded Tableau script in dom scripts
25
28
  var scripts = document.getElementsByTagName('script');
26
29
  for (var i = scripts.length; i--; ) {
27
30
  // eslint-disable-next-line eqeqeq
28
- if (scripts[i].id == `tableauJS`) return true;
31
+ if (
32
+ scripts[i].src ===
33
+ `https://public.tableau.com/javascripts/api/tableau-${version}.min.js`
34
+ )
35
+ return true;
29
36
  }
30
37
  return false;
31
38
  };
package/src/index.js CHANGED
@@ -1,6 +1,10 @@
1
1
  import sliderSVG from '@plone/volto/icons/slider.svg';
2
2
  import TableauEdit from './TableauBlock/Edit';
3
3
  import TableauView from './TableauBlock/View';
4
+ import EmbedTableauView from './Blocks/EmbedEEATableauBlock/View';
5
+ import EmbedTableauEdit from './Blocks/EmbedEEATableauBlock/Edit';
6
+ import { VisualizationView } from './Views';
7
+ import { VisualizationWidget } from './Widgets';
4
8
 
5
9
  import tableauStore from './store';
6
10
 
@@ -44,6 +48,31 @@ const applyConfig = (config) => {
44
48
  },
45
49
  };
46
50
 
51
+ config.blocks.blocksConfig.embed_eea_tableau_block = {
52
+ id: 'embed_eea_tableau_block',
53
+ title: 'Embed EEA Tableau',
54
+ icon: sliderSVG,
55
+ group: 'common',
56
+ edit: EmbedTableauEdit,
57
+ view: EmbedTableauView,
58
+ restricted: false,
59
+ mostUsed: false,
60
+ sidebarTab: 1,
61
+ blocks: {},
62
+ security: {
63
+ addPermission: [],
64
+ view: [],
65
+ },
66
+ breakpoints: {
67
+ desktop: [Infinity, 982],
68
+ tablet: [981, 768],
69
+ mobile: [767, 0],
70
+ },
71
+ };
72
+
73
+ config.views.contentTypesViews.tableau_visualization = VisualizationView;
74
+ config.widgets.id.tableau_visualization_data = VisualizationWidget;
75
+
47
76
  return config;
48
77
  };
49
78
 
@@ -49,4 +49,59 @@
49
49
  }
50
50
  }
51
51
 
52
+ .not_displayed_tableau {
53
+ width: 100%;
54
+ min-width: 633px;
55
+ height: 100%;
56
+ min-height: 200px;
57
+ background-color: #ebebeb;
58
+ }
59
+
60
+ .tableau-block {
61
+ .tableau-info {
62
+ p.tableau-error {
63
+ padding: 10px;
64
+ }
65
+ }
66
+
67
+ .tableau-loader {
68
+ display: flex;
69
+ overflow: hidden;
70
+ width: 100%;
71
+ height: 100%;
72
+ align-items: center;
73
+ justify-content: center;
74
+ margin: auto;
75
+ animation: anim 1s linear infinite;
76
+ background-image: linear-gradient(
77
+ -45deg,
78
+ #5ac5f1 25%,
79
+ #96bbde 25%,
80
+ #96bbde 50%,
81
+ #5ac5f1 50%,
82
+ #5ac5f1 75%,
83
+ #96bbde 75%
84
+ );
85
+ background-size: 100px 100px;
86
+ border-radius: 5px;
87
+ }
88
+ }
89
+
90
+ .tableau-loader span {
91
+ margin: 6px auto;
92
+ color: white;
93
+ font-weight: bold;
94
+ text-align: center;
95
+ }
96
+
97
+ @keyframes anim {
98
+ 0% {
99
+ background-position: 0 0;
100
+ }
101
+
102
+ 100% {
103
+ background-position: 50px 50px;
104
+ }
105
+ }
106
+
52
107
  .loadAddonOverrides();