@eeacms/volto-embed 6.0.1 → 7.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,89 @@
1
+ import React from 'react';
2
+ import { Popup, Input, Button } from 'semantic-ui-react';
3
+ import cx from 'classnames';
4
+
5
+ const useCopyToClipboard = (text) => {
6
+ const [copyStatus, setCopyStatus] = React.useState('inactive');
7
+ const copy = React.useCallback(() => {
8
+ navigator.clipboard.writeText(text).then(
9
+ () => setCopyStatus('copied'),
10
+ () => setCopyStatus('failed'),
11
+ );
12
+ }, [text]);
13
+
14
+ React.useEffect(() => {
15
+ if (copyStatus === 'inactive') {
16
+ return;
17
+ }
18
+
19
+ const timeout = setTimeout(() => setCopyStatus('inactive'), 3000);
20
+
21
+ return () => clearTimeout(timeout);
22
+ }, [copyStatus]);
23
+
24
+ return [copyStatus, copy];
25
+ };
26
+
27
+ const Share = ({ href = '' }) => {
28
+ const [open, setOpen] = React.useState(false);
29
+
30
+ const CopyUrlButton = ({ className, url, buttonText }) => {
31
+ const [copyUrlStatus, copyUrl] = useCopyToClipboard(url);
32
+
33
+ if (copyUrlStatus === 'copied') {
34
+ buttonText = 'Copied!';
35
+ } else if (copyUrlStatus === 'failed') {
36
+ buttonText = 'Copy failed. Please try again.';
37
+ }
38
+
39
+ return (
40
+ <Button
41
+ primary
42
+ onClick={copyUrl}
43
+ className={cx('copy-button', className)}
44
+ >
45
+ {buttonText}
46
+ </Button>
47
+ );
48
+ };
49
+
50
+ return (
51
+ <Popup
52
+ popper={{ id: 'vis-toolbar-popup', className: 'share-popup' }}
53
+ position="bottom left"
54
+ on="click"
55
+ open={open}
56
+ onClose={() => {
57
+ setOpen(false);
58
+ }}
59
+ onOpen={() => {
60
+ setOpen(true);
61
+ }}
62
+ trigger={
63
+ <div className="share">
64
+ <button className={cx('trigger-button', { open })}>
65
+ <i class="ri-share-fill"></i>
66
+ <span>Share</span>
67
+ </button>
68
+ </div>
69
+ }
70
+ content={
71
+ <>
72
+ <div className="item">
73
+ <span className="label">Copy link</span>
74
+ <div className="control">
75
+ <Input className="share-link" value={href} />
76
+ <CopyUrlButton
77
+ className="copy-button"
78
+ url={href}
79
+ buttonText="Copy"
80
+ />
81
+ </div>
82
+ </div>
83
+ </>
84
+ }
85
+ />
86
+ );
87
+ };
88
+
89
+ export default Share;
@@ -0,0 +1,74 @@
1
+ import React from 'react';
2
+ import cx from 'classnames';
3
+ import { Popup } from 'semantic-ui-react';
4
+ import { UniversalLink } from '@plone/volto/components';
5
+
6
+ const Link = ({ children, ...props }) => {
7
+ if (props.href) {
8
+ return <UniversalLink {...props}>{children}</UniversalLink>;
9
+ }
10
+ return <span {...props}>{children}</span>;
11
+ };
12
+
13
+ const Source = ({ source }) => {
14
+ if (source.chart_source_link && source.chart_source) {
15
+ return <Link href={source.chart_source_link}>{source.chart_source}</Link>;
16
+ }
17
+ if (source.chart_source) {
18
+ return <span>{source.chart_source}</span>;
19
+ }
20
+ return (
21
+ <>
22
+ <Link className="embed-sources-param-title" href={source.link}>
23
+ {source.title}
24
+ </Link>
25
+ {source.organisation && (
26
+ <>
27
+ ,{' '}
28
+ <span className="embed-sources-param-description">
29
+ {source.organisation}
30
+ </span>
31
+ </>
32
+ )}
33
+ </>
34
+ );
35
+ };
36
+
37
+ export default function Sources({ sources }) {
38
+ const [open, setOpen] = React.useState(false);
39
+
40
+ return (
41
+ <Popup
42
+ popper={{ id: 'vis-toolbar-popup', className: 'sources-popup' }}
43
+ position="bottom left"
44
+ on="click"
45
+ open={open}
46
+ onClose={() => {
47
+ setOpen(false);
48
+ }}
49
+ onOpen={() => {
50
+ setOpen(true);
51
+ }}
52
+ trigger={
53
+ <div className="sources">
54
+ <button className={cx('trigger-button', { open })}>Sources</button>
55
+ </div>
56
+ }
57
+ content={
58
+ sources?.length ? (
59
+ <ol className="sources-list">
60
+ {sources?.map((source, index) => {
61
+ return (
62
+ <li key={index}>
63
+ <Source source={source} />
64
+ </li>
65
+ );
66
+ })}
67
+ </ol>
68
+ ) : (
69
+ <p>Data provenance is not set for this visualization.</p>
70
+ )
71
+ }
72
+ />
73
+ );
74
+ }
@@ -0,0 +1,5 @@
1
+ export { default as FigureNote } from './FigureNote';
2
+ export { default as Sources } from './Sources';
3
+ export { default as MoreInfo } from './MoreInfo';
4
+ export { default as Share } from './Share';
5
+ export { default as Enlarge } from './Enlarge';
@@ -0,0 +1,209 @@
1
+ @import (multiple, reference, optional) '../../theme.config';
2
+
3
+ @type: extra;
4
+ @element: custom;
5
+
6
+ .enlarge-modal.ui.modal {
7
+ width: 95% !important;
8
+ height: 85%;
9
+
10
+ .close.icon {
11
+ top: 1rem;
12
+ right: 1rem;
13
+ width: 24px !important;
14
+ height: 24px;
15
+ padding: 0;
16
+
17
+ > i {
18
+ font-size: 24px;
19
+ }
20
+ }
21
+
22
+ .content {
23
+ height: 90%;
24
+
25
+ .js-plotly-plot {
26
+ width: 100%;
27
+ height: 100%;
28
+ }
29
+ }
30
+ }
31
+
32
+ .visualization-toolbar,
33
+ .legend-toolbar {
34
+ display: flex;
35
+ justify-content: space-between;
36
+ margin-bottom: 1rem;
37
+
38
+ .left-col {
39
+ > *:not(:last-child)::after {
40
+ width: 1px;
41
+ margin: 4px 0;
42
+ background-color: @textColor;
43
+ content: '';
44
+ }
45
+ }
46
+
47
+ .left-col,
48
+ .right-col {
49
+ display: flex;
50
+ align-items: center;
51
+ column-gap: 1rem;
52
+
53
+ > * {
54
+ display: flex;
55
+ height: 100%;
56
+ align-items: stretch;
57
+ column-gap: 1rem;
58
+ }
59
+ }
60
+
61
+ .trigger-button {
62
+ display: inline-flex;
63
+ align-items: center;
64
+ padding: 0;
65
+ padding-bottom: 3px;
66
+ border: none;
67
+ background-color: transparent;
68
+ color: @textColor;
69
+ column-gap: 0.25rem;
70
+ cursor: pointer;
71
+ line-height: 1.5;
72
+
73
+ &:hover {
74
+ color: @secondaryColor;
75
+ }
76
+
77
+ &.open {
78
+ padding-bottom: 0;
79
+ border-bottom: 3px solid @secondaryColor;
80
+ color: @secondaryColor;
81
+ }
82
+
83
+ i {
84
+ line-height: 1;
85
+ }
86
+ }
87
+ }
88
+
89
+ .legend-toolbar {
90
+ display: block;
91
+
92
+ &:not(.open) {
93
+ margin-bottom: 0.25rem;
94
+ }
95
+
96
+ .trigger-button {
97
+ position: relative;
98
+ z-index: 1;
99
+
100
+ &.open {
101
+ margin-bottom: 0.5rem;
102
+ }
103
+ }
104
+ }
105
+
106
+ #vis-toolbar-popup {
107
+ .ui.popup {
108
+ padding-right: 1rem;
109
+ padding-left: 1rem;
110
+ background-color: @grey-1;
111
+ box-shadow: rgba(0, 0, 0, 0.15) 1.95px 1.95px 2.6px;
112
+
113
+ &::before {
114
+ background-color: @grey-1;
115
+ }
116
+ }
117
+
118
+ &.figure-note-popup,
119
+ &.sources-popup {
120
+ .ui.popup {
121
+ min-width: 600px;
122
+
123
+ @media screen and (max-width: @largestMobileScreen) {
124
+ min-width: 300px;
125
+ }
126
+ }
127
+ }
128
+
129
+ &.sources-popup {
130
+ .sources-list {
131
+ margin: 0;
132
+ list-style: decimal inside;
133
+ padding-inline-start: 0;
134
+ }
135
+ }
136
+
137
+ .item {
138
+ margin-bottom: 0.75rem;
139
+ }
140
+
141
+ .label {
142
+ display: block;
143
+ margin-bottom: 0.5rem;
144
+ }
145
+
146
+ .control {
147
+ display: flex;
148
+ flex-wrap: nowrap;
149
+ align-items: stretch;
150
+
151
+ > * {
152
+ display: flex;
153
+ margin: 0;
154
+ }
155
+ }
156
+
157
+ .types {
158
+ display: flex;
159
+ column-gap: 1rem;
160
+
161
+ > *:not(:last-child)::after {
162
+ display: block;
163
+ width: 1px;
164
+ height: 100%;
165
+ background-color: @textColor;
166
+ content: '';
167
+ }
168
+
169
+ .type {
170
+ display: flex;
171
+ column-gap: 1rem;
172
+ }
173
+
174
+ button {
175
+ display: inline-flex;
176
+ align-items: center;
177
+ padding: 0;
178
+ border: none;
179
+ background-color: transparent;
180
+ color: @linkColor;
181
+ cursor: pointer;
182
+
183
+ &:hover {
184
+ color: @linkHoverColor;
185
+ text-decoration: @linkHoverUnderline;
186
+ }
187
+ }
188
+ }
189
+ }
190
+
191
+ .visualization-wrapper.mobile {
192
+ .visualization-toolbar {
193
+ flex-flow: column;
194
+ align-items: center;
195
+ row-gap: 0.5rem;
196
+
197
+ .right-col {
198
+ .trigger-button {
199
+ flex-flow: column;
200
+ }
201
+ }
202
+ }
203
+ }
204
+
205
+ @media print {
206
+ .visualization-toolbar {
207
+ display: none;
208
+ }
209
+ }
package/src/index.js CHANGED
@@ -1,19 +1,13 @@
1
- import { ViewIframe, EditIframe } from './Iframe';
2
-
1
+ import installBlocks from './Blocks';
3
2
  export {
4
3
  PrivacyProtection,
5
4
  addPrivacyProtectionToSchema,
6
5
  } from './PrivacyProtection';
7
6
 
8
- export default (config) => {
9
- config.blocks.blocksConfig.maps = {
10
- ...config.blocks.blocksConfig.maps,
11
- view: ViewIframe,
12
- edit: EditIframe,
13
- };
7
+ export default function applyConfig(config) {
14
8
  config.settings.allowed_cors_destinations = [
15
9
  ...config.settings.allowed_cors_destinations,
16
10
  'screenshot.eea.europa.eu',
17
11
  ];
18
- return config;
19
- };
12
+ return [installBlocks].reduce((acc, apply) => apply(acc), config);
13
+ }
@@ -1,88 +0,0 @@
1
- import React from 'react';
2
- import { render, fireEvent } from '@testing-library/react';
3
- import { Provider } from 'react-intl-redux';
4
- import configureStore from 'redux-mock-store';
5
- import Edit from './EditIframe';
6
- import '@testing-library/jest-dom/extend-expect';
7
-
8
- const mockStore = configureStore();
9
-
10
- const store = mockStore(() => ({
11
- connected_data_parameters: {},
12
- router: {
13
- location: {
14
- pathname: '',
15
- },
16
- },
17
- intl: {
18
- locale: 'en',
19
- messages: {},
20
- },
21
- }));
22
-
23
- describe('Edit', () => {
24
- it('renders without crashing', () => {
25
- render(
26
- <Provider store={store}>
27
- <Provider store={store}>
28
- <Edit
29
- selected={false}
30
- block="block"
31
- index={1}
32
- data={{
33
- url: 'test',
34
- }}
35
- pathname="/"
36
- onChangeBlock={() => {}}
37
- onSelectBlock={() => {}}
38
- onDeleteBlock={() => {}}
39
- onFocusPreviousBlock={() => {}}
40
- onFocusNextBlock={() => {}}
41
- handleKeyDown={() => {}}
42
- />
43
- </Provider>
44
- </Provider>,
45
- );
46
- });
47
-
48
- it('submits url when button is clicked', () => {
49
- const { container, getByPlaceholderText } = render(
50
- <Provider store={store}>
51
- <Provider store={store}>
52
- <Edit
53
- selected={false}
54
- block="block"
55
- index={1}
56
- data={{
57
- dataprotection: {
58
- privacy_statement: 'test',
59
- },
60
- }}
61
- pathname="/"
62
- onChangeBlock={() => {}}
63
- onSelectBlock={() => {}}
64
- onDeleteBlock={() => {}}
65
- onFocusPreviousBlock={() => {}}
66
- onFocusNextBlock={() => {}}
67
- handleKeyDown={() => {}}
68
- />
69
- </Provider>
70
- </Provider>,
71
- );
72
-
73
- const input = getByPlaceholderText('Enter map Embed Code');
74
- fireEvent.click(input);
75
- fireEvent.change(input, { target: { value: 'test url' } });
76
- fireEvent.click(container.querySelector('button.cancel'));
77
- fireEvent.change(input, { target: { value: 'test url' } });
78
- fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
79
- fireEvent.keyDown(input, { key: 'Escape', code: 'Escape' });
80
- fireEvent.keyDown(input, { key: 'KeyA', code: 'KeyA' });
81
-
82
- fireEvent.change(input, { target: { value: '<iframe src="test"/>' } });
83
- fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
84
-
85
- const button = container.querySelector('button.ui.basic.primary.button');
86
- fireEvent.click(button);
87
- });
88
- });
@@ -1,4 +0,0 @@
1
- import EditIframe from './EditIframe';
2
- import ViewIframe from './ViewIframe';
3
-
4
- export { EditIframe, ViewIframe };
@@ -1,39 +0,0 @@
1
- const schema = {
2
- title: 'Embed external content',
3
-
4
- fieldsets: [
5
- {
6
- id: 'default',
7
- title: 'Default',
8
- fields: ['url', 'align', 'height'],
9
- },
10
- ],
11
-
12
- properties: {
13
- url: {
14
- title: 'Embed URL',
15
- },
16
- align: {
17
- title: 'Alignment',
18
- widget: 'align',
19
- type: 'string',
20
- },
21
- height: {
22
- title: (
23
- <a
24
- rel="noreferrer"
25
- target="_blank"
26
- href="https://developer.mozilla.org/en-US/docs/Web/CSS/height"
27
- >
28
- CSS height
29
- </a>
30
- ),
31
- default: '45vh',
32
- description: 'Iframe height',
33
- },
34
- },
35
-
36
- required: ['url'],
37
- };
38
-
39
- export default schema;