@eeacms/volto-tableau 2.0.0 → 3.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.
- package/.project.eslintrc.js +47 -0
- package/CHANGELOG.md +28 -1
- package/cypress/support/commands.js +13 -12
- package/package.json +1 -1
- package/src/Blocks/EmbedEEATableauBlock/Edit.jsx +55 -0
- package/src/Blocks/EmbedEEATableauBlock/View.jsx +55 -0
- package/src/Blocks/EmbedEEATableauBlock/schema.js +31 -0
- package/src/ConnectedTableau/ConnectedTableau.jsx +21 -0
- package/src/Tableau/View.jsx +16 -2
- package/src/TableauBlock/View.jsx +5 -0
- package/src/Views/VisualizationView.jsx +43 -0
- package/src/Views/index.js +3 -0
- package/src/Widgets/VisualizationWidget.jsx +158 -0
- package/src/Widgets/index.js +3 -0
- package/src/Widgets/schema.js +176 -0
- package/src/Widgets/style.less +8 -0
- package/src/helpers.js +27 -7
- package/src/index.js +29 -0
- package/src/less/tableau.less +55 -0
|
@@ -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,34 @@ 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
|
-
### [
|
|
7
|
+
### [3.0.1](https://github.com/eea/volto-tableau/compare/3.0.0...3.0.1) - 13 January 2023
|
|
8
|
+
|
|
9
|
+
#### :hammer_and_wrench: Others
|
|
10
|
+
|
|
11
|
+
- helper comments [andreiggr - [`b13abfd`](https://github.com/eea/volto-tableau/commit/b13abfd3fe5813f872b98de66de63c98f4721c62)]
|
|
12
|
+
- wait for client before document get [andreiggr - [`e12a3db`](https://github.com/eea/volto-tableau/commit/e12a3dbd302a33e446cc01da914d90b8cf54a024)]
|
|
13
|
+
## [3.0.0](https://github.com/eea/volto-tableau/compare/2.0.0...3.0.0) - 13 January 2023
|
|
14
|
+
|
|
15
|
+
#### :hammer_and_wrench: Others
|
|
16
|
+
|
|
17
|
+
- Remove sources from schema [dana-cfc4 - [`0d2b862`](https://github.com/eea/volto-tableau/commit/0d2b86212b72e06d4ab81804395eda0c2919aeb8)]
|
|
18
|
+
- setup the loader, script loading, data provenance, error catching [andreiggr - [`bc65898`](https://github.com/eea/volto-tableau/commit/bc65898edf878f8d011573f91fb72fb1a956d8e9)]
|
|
19
|
+
- merge with better_tableau_import [andreiggr - [`2d8f314`](https://github.com/eea/volto-tableau/commit/2d8f314a939f1122c0307ccfebf4215bcea2c148)]
|
|
20
|
+
- better loader response [andreiggr - [`d6f3c86`](https://github.com/eea/volto-tableau/commit/d6f3c86cc97475819db71a99108e4da739d66576)]
|
|
21
|
+
- Add ConnectedTableau [dana-cfc4 - [`d83417d`](https://github.com/eea/volto-tableau/commit/d83417d1b9ea454ded0b6f24e581ac2a85c32780)]
|
|
22
|
+
- lint [andreiggr - [`f268b10`](https://github.com/eea/volto-tableau/commit/f268b10710a77cd19d9cc110d60447d495a747ed)]
|
|
23
|
+
- fix crashing sources && better getContent [andreiggr - [`a202967`](https://github.com/eea/volto-tableau/commit/a202967340bcb8e366868193d236d9f49b563bfc)]
|
|
24
|
+
- somewhat better import [andreiggr - [`48219a3`](https://github.com/eea/volto-tableau/commit/48219a368b7165ef9e9df3cf65e643be72f5f8b0)]
|
|
25
|
+
- Fix style lint errors [dana-cfc4 - [`ed1873c`](https://github.com/eea/volto-tableau/commit/ed1873ce27c81e6f2a2459f38bc4cd10d65b559b)]
|
|
26
|
+
- Fix initValue not initialized [dana-cfc4 - [`bbe5802`](https://github.com/eea/volto-tableau/commit/bbe5802c8e2ee794b15570959a00307531b32d96)]
|
|
27
|
+
- Add placeholder in case of errors [dana-cfc4 - [`1af2d0d`](https://github.com/eea/volto-tableau/commit/1af2d0da04635716f4aef320c5f84694a59435e7)]
|
|
28
|
+
- importmap for versions of tableau [Andrei Grigore - [`4e03cb2`](https://github.com/eea/volto-tableau/commit/4e03cb2c3a3bf11d186462067d12c2685b9bf607)]
|
|
29
|
+
- Increase addon version [danac - [`26b58c5`](https://github.com/eea/volto-tableau/commit/26b58c57b6cb4be4eed23fe3343c65250e6d593a)]
|
|
30
|
+
- Add data provenance [danac - [`bd46995`](https://github.com/eea/volto-tableau/commit/bd4699547be72b2d9dad292ee3681b937e741239)]
|
|
31
|
+
- Add eslint config [danac - [`394e5d1`](https://github.com/eea/volto-tableau/commit/394e5d11591610a97cb5b57d882da818b0128b7e)]
|
|
32
|
+
- Fix formatting issues [danac - [`2ef1bae`](https://github.com/eea/volto-tableau/commit/2ef1bae5caf44fb144f8251aed2895df2e03de7d)]
|
|
33
|
+
- Add Embed EEA Tableau block [danac - [`54e45b2`](https://github.com/eea/volto-tableau/commit/54e45b2b0b29d01173315ffe47e74938e43cdf23)]
|
|
34
|
+
## [2.0.0](https://github.com/eea/volto-tableau/compare/1.3.0...2.0.0) - 1 November 2022
|
|
8
35
|
|
|
9
36
|
#### :bug: Bug Fixes
|
|
10
37
|
|
|
@@ -71,7 +71,8 @@ Cypress.Commands.add(
|
|
|
71
71
|
id: contentId,
|
|
72
72
|
title: contentTitle,
|
|
73
73
|
image: {
|
|
74
|
-
data:
|
|
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
|
@@ -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;
|
package/src/Tableau/View.jsx
CHANGED
|
@@ -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 {
|
|
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 =
|
|
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,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,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
|
+
};
|
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,16 +18,36 @@ 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 = (
|
|
26
|
+
const isMyScriptLoaded = (version) => {
|
|
24
27
|
//check for loaded Tableau script in dom scripts
|
|
25
|
-
var scripts = document.getElementsByTagName('script');
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
var scripts = __CLIENT__ && document.getElementsByTagName('script');
|
|
29
|
+
if (scripts) {
|
|
30
|
+
for (var i = scripts.length; i--; ) {
|
|
31
|
+
// eslint-disable-next-line eqeqeq
|
|
32
|
+
if (
|
|
33
|
+
scripts[i].src ===
|
|
34
|
+
`https://public.tableau.com/javascripts/api/tableau-${version}.min.js`
|
|
35
|
+
)
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
29
38
|
}
|
|
30
39
|
return false;
|
|
31
40
|
};
|
|
32
41
|
|
|
33
42
|
export { loadTableauScript, isMyScriptLoaded };
|
|
43
|
+
|
|
44
|
+
//script url for each version. In case you might need to add them in the load balancer
|
|
45
|
+
// https://public.tableau.com/javascripts/api/tableau-2.8.0.min.js
|
|
46
|
+
// https://public.tableau.com/javascripts/api/tableau-2.7.0.min.js
|
|
47
|
+
// https://public.tableau.com/javascripts/api/tableau-2.6.0.min.js
|
|
48
|
+
// https://public.tableau.com/javascripts/api/tableau-2.5.0.min.js
|
|
49
|
+
// https://public.tableau.com/javascripts/api/tableau-2.4.0.min.js
|
|
50
|
+
// https://public.tableau.com/javascripts/api/tableau-2.3.0.min.js
|
|
51
|
+
// https://public.tableau.com/javascripts/api/tableau-2.2.2.min.js
|
|
52
|
+
// https://public.tableau.com/javascripts/api/tableau-2.1.2.min.js
|
|
53
|
+
// https://public.tableau.com/javascripts/api/tableau-2.0.3.min.js
|
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
|
|
package/src/less/tableau.less
CHANGED
|
@@ -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();
|