@plone/volto 17.0.0-alpha.20 → 17.0.0-alpha.22

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.
Files changed (108) hide show
  1. package/.gitignore~ +71 -0
  2. package/.yarn/install-state.gz +0 -0
  3. package/CHANGELOG.md +49 -0
  4. package/cypress/support/commands.js +2 -1
  5. package/cypress/support/e2e.js +1 -2
  6. package/locales/ca/LC_MESSAGES/volto.po +24 -5
  7. package/locales/ca.json +1 -1
  8. package/locales/de/LC_MESSAGES/volto.po +37 -18
  9. package/locales/de.json +1 -1
  10. package/locales/en/LC_MESSAGES/volto.po +25 -6
  11. package/locales/en.json +1 -1
  12. package/locales/es/LC_MESSAGES/volto.po +25 -6
  13. package/locales/es.json +1 -1
  14. package/locales/eu/LC_MESSAGES/volto.po +24 -5
  15. package/locales/eu.json +1 -1
  16. package/locales/fi/LC_MESSAGES/volto.po +24 -5
  17. package/locales/fi.json +1 -1
  18. package/locales/fr/LC_MESSAGES/volto.po +24 -5
  19. package/locales/fr.json +1 -1
  20. package/locales/it/LC_MESSAGES/volto.po +250 -231
  21. package/locales/it.json +1 -1
  22. package/locales/ja/LC_MESSAGES/volto.po +24 -5
  23. package/locales/ja.json +1 -1
  24. package/locales/nl/LC_MESSAGES/volto.po +24 -5
  25. package/locales/nl.json +1 -1
  26. package/locales/pt/LC_MESSAGES/volto.po +24 -5
  27. package/locales/pt.json +1 -1
  28. package/locales/pt_BR/LC_MESSAGES/volto.po +25 -6
  29. package/locales/pt_BR.json +1 -1
  30. package/locales/ro/LC_MESSAGES/volto.po +24 -5
  31. package/locales/ro.json +1 -1
  32. package/locales/volto.pot +25 -6
  33. package/locales/zh_CN/LC_MESSAGES/volto.po +24 -5
  34. package/locales/zh_CN.json +1 -1
  35. package/news/4547.breaking~ +1 -0
  36. package/package.json +6 -6
  37. package/packages/volto-slate/package.json +1 -1
  38. package/src/actions/relations/rebuild.js +7 -7
  39. package/src/components/index.js +1 -0
  40. package/src/components/manage/Actions/Actions.jsx +133 -243
  41. package/src/components/manage/Blocks/Container/Edit.jsx +4 -1
  42. package/src/components/manage/Blocks/Container/EditBlockWrapper.jsx +1 -0
  43. package/src/components/manage/Blocks/Grid/Edit.jsx +13 -1
  44. package/src/components/manage/Blocks/Image/Edit.jsx +40 -5
  45. package/src/components/manage/Blocks/Image/Edit.test.jsx +2 -0
  46. package/src/components/manage/Blocks/Image/ImageSidebar.jsx +64 -15
  47. package/src/components/manage/Blocks/Image/View.jsx +26 -5
  48. package/src/components/manage/Blocks/Image/View.test.jsx +20 -0
  49. package/src/components/manage/Blocks/Image/schema.js +1 -9
  50. package/src/components/manage/Blocks/Image/utils.js +14 -0
  51. package/src/components/manage/Blocks/LeadImage/Edit.jsx +32 -10
  52. package/src/components/manage/Blocks/LeadImage/Edit.test.jsx +11 -1
  53. package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.jsx +28 -9
  54. package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.test.jsx +8 -2
  55. package/src/components/manage/Blocks/LeadImage/View.jsx +50 -38
  56. package/src/components/manage/Blocks/LeadImage/View.test.jsx +11 -1
  57. package/src/components/manage/Blocks/Listing/SummaryTemplate.jsx +1 -1
  58. package/src/components/manage/Blocks/Maps/Edit.jsx +135 -209
  59. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +3 -2
  60. package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +13 -23
  61. package/src/components/manage/Contents/Contents.jsx +8 -6
  62. package/src/components/manage/Contents/ContentsPropertiesModal.jsx +1 -13
  63. package/src/components/manage/Controlpanels/Groups/RenderGroups.jsx +2 -2
  64. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +30 -7
  65. package/src/components/manage/Controlpanels/Relations/Relations.jsx +2 -2
  66. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +53 -59
  67. package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +2 -2
  68. package/src/components/manage/Delete/Delete.jsx +96 -171
  69. package/src/components/manage/Sidebar/AlignBlock.jsx +1 -1
  70. package/src/components/manage/Widgets/SelectUtils.js +1 -1
  71. package/src/components/manage/Workflow/Workflow.jsx +75 -184
  72. package/src/components/theme/Image/Image.jsx +96 -0
  73. package/src/components/theme/Image/Image.test.jsx +125 -0
  74. package/src/components/theme/Logo/Logo.jsx +2 -0
  75. package/src/components/theme/PasswordReset/RequestPasswordReset.jsx +95 -170
  76. package/src/components/theme/PreviewImage/PreviewImage.jsx +25 -14
  77. package/src/components/theme/PreviewImage/PreviewImage.test.js +39 -16
  78. package/src/components/theme/View/AlbumView.jsx +11 -15
  79. package/src/components/theme/View/EventView.jsx +30 -23
  80. package/src/components/theme/View/ImageView.jsx +5 -2
  81. package/src/components/theme/View/ImageView.test.jsx +4 -0
  82. package/src/components/theme/View/ListingView.jsx +5 -3
  83. package/src/components/theme/View/NewsItemView.jsx +7 -13
  84. package/src/components/theme/View/SummaryView.jsx +4 -3
  85. package/src/config/Blocks.jsx +2 -0
  86. package/src/config/Components.jsx +3 -1
  87. package/src/config/index.js~ +223 -0
  88. package/src/express-middleware/files.js +8 -6
  89. package/src/express-middleware/images.js +7 -1
  90. package/src/helpers/MessageLabels/MessageLabels.js +6 -0
  91. package/src/helpers/Url/Url.js +22 -1
  92. package/src/helpers/Url/Url.test.js +41 -0
  93. package/src/reducers/relations/relations.js +1 -1
  94. package/test-setup-config.js +9 -1
  95. package/theme/themes/pastanaga/extras/blocks.less +3 -1
  96. package/theme/themes/pastanaga/extras/main.less +5 -0
  97. package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +0 -90
  98. package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +0 -6
  99. package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +0 -6
  100. package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +0 -6
  101. package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +0 -10
  102. package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +0 -10
  103. package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +0 -30
  104. package/packages/volto-slate/build/messages/src/elementEditor/messages.json +0 -10
  105. package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +0 -6
  106. package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +0 -6
  107. package/src/components/manage/Blocks/Teaser/utils.js +0 -44
  108. package/src/components/manage/Blocks/Teaser/utils.test.jsx +0 -229
@@ -1,22 +1,17 @@
1
- /**
2
- * Workflow component.
3
- * @module components/manage/Workflow/Workflow
4
- */
5
-
6
- import React, { Component, Fragment } from 'react';
1
+ import { useEffect } from 'react';
7
2
  import PropTypes from 'prop-types';
8
3
  import { compose } from 'redux';
9
- import { connect } from 'react-redux';
4
+ import { useDispatch, useSelector, shallowEqual } from 'react-redux';
10
5
  import { uniqBy } from 'lodash';
11
6
  import { toast } from 'react-toastify';
12
- import { defineMessages, injectIntl } from 'react-intl';
7
+ import { defineMessages, useIntl } from 'react-intl';
8
+
13
9
  import { FormFieldWrapper, Icon, Toast } from '@plone/volto/components';
14
10
  import {
15
11
  flattenToAppURL,
16
- getCurrentStateMapping,
17
12
  getWorkflowOptions,
13
+ getCurrentStateMapping,
18
14
  } from '@plone/volto/helpers';
19
-
20
15
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
21
16
 
22
17
  import {
@@ -24,7 +19,6 @@ import {
24
19
  getWorkflow,
25
20
  transitionWorkflow,
26
21
  } from '@plone/volto/actions';
27
-
28
22
  import downSVG from '@plone/volto/icons/down-key.svg';
29
23
  import upSVG from '@plone/volto/icons/up-key.svg';
30
24
  import checkSVG from '@plone/volto/icons/check.svg';
@@ -165,191 +159,88 @@ const customSelectStyles = {
165
159
  }),
166
160
  };
167
161
 
168
- /**
169
- * Workflow container class.
170
- * @class Workflow
171
- * @extends Component
172
- */
173
- class Workflow extends Component {
174
- /**
175
- * Property types.
176
- * @property {Object} propTypes Property types.
177
- * @static
178
- */
179
- static propTypes = {
180
- getContent: PropTypes.func.isRequired,
181
- getWorkflow: PropTypes.func.isRequired,
182
- transitionWorkflow: PropTypes.func.isRequired,
183
- loaded: PropTypes.bool.isRequired,
184
- pathname: PropTypes.string.isRequired,
185
- history: PropTypes.arrayOf(
186
- PropTypes.shape({
187
- review_state: PropTypes.string,
188
- }),
189
- ),
190
- transitions: PropTypes.arrayOf(
191
- PropTypes.shape({
192
- '@id': PropTypes.string,
193
- title: PropTypes.string,
194
- }),
195
- ),
196
- };
162
+ function useWorkflow() {
163
+ const history = useSelector((state) => state.workflow.history, shallowEqual);
164
+ const transitions = useSelector(
165
+ (state) => state.workflow.transitions,
166
+ shallowEqual,
167
+ );
168
+ const loaded = useSelector((state) => state.workflow.transition.loaded);
169
+ const currentStateValue = useSelector(
170
+ (state) => getCurrentStateMapping(state.workflow.currentState),
171
+ shallowEqual,
172
+ );
197
173
 
198
- /**
199
- * Default properties
200
- * @property {Object} defaultProps Default properties.
201
- * @static
202
- */
203
- static defaultProps = {
204
- history: [],
205
- transitions: [],
206
- };
174
+ return { loaded, history, transitions, currentStateValue };
175
+ }
207
176
 
208
- componentDidMount() {
209
- this.props.getWorkflow(this.props.pathname);
210
- }
177
+ const Workflow = (props) => {
178
+ const intl = useIntl();
179
+ const dispatch = useDispatch();
180
+ const { loaded, transitions, currentStateValue } = useWorkflow();
181
+ const content = useSelector((state) => state.content?.data, shallowEqual);
182
+ const { pathname } = props;
211
183
 
212
- /**
213
- * Component will receive props
214
- * @method componentWillReceiveProps
215
- * @param {Object} nextProps Next properties
216
- * @returns {undefined}
217
- */
218
- UNSAFE_componentWillReceiveProps(nextProps) {
219
- if (nextProps.pathname !== this.props.pathname) {
220
- this.props.getWorkflow(nextProps.pathname);
221
- }
222
- if (!this.props.loaded && nextProps.loaded) {
223
- this.props.getWorkflow(nextProps.pathname);
224
- this.props.getContent(nextProps.pathname);
225
- }
226
- }
184
+ useEffect(() => {
185
+ dispatch(getWorkflow(pathname));
186
+ dispatch(getContent(pathname));
187
+ }, [dispatch, pathname, loaded]);
227
188
 
228
- /**
229
- * On transition handler
230
- * @method transition
231
- * @param {string} event Event object
232
- * @returns {undefined}
233
- */
234
- transition = (selectedOption) => {
235
- this.props.transitionWorkflow(flattenToAppURL(selectedOption.url));
189
+ const transition = (selectedOption) => {
190
+ dispatch(transitionWorkflow(flattenToAppURL(selectedOption.url)));
236
191
  toast.success(
237
192
  <Toast
238
193
  success
239
- title={this.props.intl.formatMessage(messages.messageUpdated)}
194
+ title={intl.formatMessage(messages.messageUpdated)}
240
195
  content=""
241
196
  />,
242
197
  );
243
198
  };
244
199
 
245
- selectValue = (option) => {
246
- const stateDecorator = {
247
- marginLeft: '10px',
248
- marginRight: '10px',
249
- display: 'inline-block',
250
- backgroundColor: option.color || null,
251
- content: ' ',
252
- height: '10px',
253
- width: '10px',
254
- borderRadius: '50%',
255
- };
256
- return (
257
- <Fragment>
258
- <span style={stateDecorator} />
259
- <span className="Select-value-label">{option.label}</span>
260
- </Fragment>
261
- );
262
- };
263
-
264
- optionRenderer = (option) => {
265
- const stateDecorator = {
266
- marginLeft: '10px',
267
- marginRight: '10px',
268
- display: 'inline-block',
269
- backgroundColor:
270
- this.props.currentStateValue.value === option.value
271
- ? option.color
272
- : null,
273
- content: ' ',
274
- height: '10px',
275
- width: '10px',
276
- borderRadius: '50%',
277
- border:
278
- this.props.currentStateValue.value !== option.value
279
- ? `1px solid ${option.color}`
280
- : null,
281
- };
282
-
283
- return (
284
- <Fragment>
285
- <span style={stateDecorator} />
286
- <span style={{ marginRight: 'auto' }}>{option.label}</span>
287
- <Icon name={checkSVG} size="24px" />
288
- </Fragment>
289
- );
290
- };
200
+ const { Placeholder } = props.reactSelect.components;
201
+ const Select = props.reactSelect.default;
291
202
 
292
- render() {
293
- const { Placeholder } = this.props.reactSelect.components;
294
- const Select = this.props.reactSelect.default;
203
+ return (
204
+ <FormFieldWrapper
205
+ id="state-select"
206
+ title={intl.formatMessage(messages.state)}
207
+ intl={intl}
208
+ {...props}
209
+ >
210
+ <Select
211
+ name="state-select"
212
+ className="react-select-container"
213
+ classNamePrefix="react-select"
214
+ isDisabled={!content.review_state || transitions.length === 0}
215
+ options={uniqBy(
216
+ transitions.map((transition) => getWorkflowOptions(transition)),
217
+ 'label',
218
+ ).concat(currentStateValue)}
219
+ styles={customSelectStyles}
220
+ theme={selectTheme}
221
+ components={{
222
+ DropdownIndicator,
223
+ Placeholder,
224
+ Option,
225
+ SingleValue,
226
+ }}
227
+ onChange={transition}
228
+ value={
229
+ content.review_state
230
+ ? currentStateValue
231
+ : {
232
+ label: intl.formatMessage(messages.messageNoWorkflow),
233
+ value: 'noworkflow',
234
+ }
235
+ }
236
+ isSearchable={false}
237
+ />
238
+ </FormFieldWrapper>
239
+ );
240
+ };
295
241
 
296
- return (
297
- <FormFieldWrapper
298
- id="state-select"
299
- title={this.props.intl.formatMessage(messages.state)}
300
- {...this.props}
301
- >
302
- <Select
303
- name="state-select"
304
- className="react-select-container"
305
- classNamePrefix="react-select"
306
- isDisabled={
307
- !this.props.content.review_state ||
308
- this.props.transitions.length === 0
309
- }
310
- options={uniqBy(
311
- this.props.transitions.map((transition) =>
312
- getWorkflowOptions(transition),
313
- ),
314
- 'label',
315
- ).concat(this.props.currentStateValue)}
316
- styles={customSelectStyles}
317
- theme={selectTheme}
318
- components={{
319
- DropdownIndicator,
320
- Placeholder,
321
- Option,
322
- SingleValue,
323
- }}
324
- onChange={this.transition}
325
- value={
326
- this.props.content.review_state
327
- ? this.props.currentStateValue
328
- : {
329
- label: this.props.intl.formatMessage(
330
- messages.messageNoWorkflow,
331
- ),
332
- value: 'noworkflow',
333
- }
334
- }
335
- isSearchable={false}
336
- />
337
- </FormFieldWrapper>
338
- );
339
- }
340
- }
242
+ Workflow.propTypes = {
243
+ pathname: PropTypes.string.isRequired,
244
+ };
341
245
 
342
- export default compose(
343
- injectIntl,
344
- injectLazyLibs(['reactSelect']),
345
- connect(
346
- (state) => ({
347
- loaded: state.workflow.transition.loaded,
348
- content: state.content.data,
349
- history: state.workflow.history,
350
- transitions: state.workflow.transitions,
351
- currentStateValue: getCurrentStateMapping(state.workflow.currentState),
352
- }),
353
- { getContent, getWorkflow, transitionWorkflow },
354
- ),
355
- )(Workflow);
246
+ export default compose(injectLazyLibs(['reactSelect']))(Workflow);
@@ -0,0 +1,96 @@
1
+ import PropTypes from 'prop-types';
2
+ import cx from 'classnames';
3
+ import { flattenToAppURL } from '@plone/volto/helpers';
4
+
5
+ /**
6
+ * Image component
7
+ * @param {object} item - Context item that has the image field (can also be a catalog brain or summary)
8
+ * @param {string} imageField - Key of the image field inside the item, or inside the image_scales object of the item if it is a catalog brain or summary
9
+ * @param {string} src - URL of the image to be used if the item field is not available
10
+ * @param {string} alt - Alternative text for the image
11
+ * @param {boolean} loading - (default: eager) set to `lazy` to lazy load the image
12
+ * @param {boolean} responsive - (default: false) set to `true` to add the `responsive` class to the image
13
+ * @param {string} className - Additional classes to add to the image
14
+ */
15
+ export default function Image({
16
+ item,
17
+ imageField,
18
+ src,
19
+ alt = '',
20
+ loading = 'eager',
21
+ responsive = false,
22
+ className = '',
23
+ ...imageProps
24
+ }) {
25
+ if (!item && !src) return null;
26
+
27
+ // TypeScript hints for editor autocomplete :)
28
+ /** @type {React.ImgHTMLAttributes<HTMLImageElement>} */
29
+ const attrs = {};
30
+
31
+ if (!item && src) {
32
+ attrs.src = src;
33
+ attrs.className = cx(className, { responsive });
34
+ } else {
35
+ const isFromRealObject = !item.image_scales;
36
+ const imageFieldWithDefault = imageField || item.image_field || 'image';
37
+
38
+ const image = isFromRealObject
39
+ ? item[imageFieldWithDefault]
40
+ : item.image_scales[imageFieldWithDefault]?.[0];
41
+
42
+ if (!image) return null;
43
+
44
+ const isSvg = image['content-type'] === 'image/svg+xml';
45
+
46
+ const baseUrl = isFromRealObject ? '' : flattenToAppURL(item['@id'] + '/');
47
+
48
+ attrs.src = `${baseUrl}${flattenToAppURL(image.download)}`;
49
+ attrs.width = image.width;
50
+ attrs.height = image.height;
51
+ attrs.style = {
52
+ aspectRatio: `${image.width} / ${image.height}`,
53
+ ...imageProps.style,
54
+ };
55
+ attrs.className = cx(className, { responsive });
56
+
57
+ if (!isSvg && image.scales && Object.keys(image.scales).length > 0) {
58
+ const sortedScales = Object.values(image.scales).sort((a, b) => {
59
+ if (a.width > b.width) return 1;
60
+ else if (a.width < b.width) return -1;
61
+ else return 0;
62
+ });
63
+
64
+ attrs.srcSet = sortedScales
65
+ .map(
66
+ (scale) =>
67
+ `${baseUrl}${flattenToAppURL(scale.download)} ${scale.width}w`,
68
+ )
69
+ .join(', ');
70
+ }
71
+ }
72
+
73
+ if (loading === 'lazy') {
74
+ attrs.loading = 'lazy';
75
+ attrs.decoding = 'async';
76
+ } else {
77
+ attrs.fetchpriority = 'high';
78
+ }
79
+
80
+ return <img {...attrs} alt={alt} {...imageProps} />;
81
+ }
82
+
83
+ Image.propTypes = {
84
+ item: PropTypes.shape({
85
+ '@id': PropTypes.string,
86
+ image_field: PropTypes.string,
87
+ image_scales: PropTypes.object,
88
+ image: PropTypes.object,
89
+ }),
90
+ imageField: PropTypes.string,
91
+ src: PropTypes.string,
92
+ alt: PropTypes.string.isRequired,
93
+ loading: PropTypes.string,
94
+ responsive: PropTypes.bool,
95
+ className: PropTypes.string,
96
+ };
@@ -0,0 +1,125 @@
1
+ import React from 'react';
2
+ import renderer from 'react-test-renderer';
3
+ import Image from './Image';
4
+
5
+ test('renders an image component with fetchpriority high', () => {
6
+ const component = renderer.create(
7
+ <Image
8
+ item={{
9
+ image: {
10
+ download: 'http://localhost:3000/image/@@images/image/image.png',
11
+ width: 400,
12
+ height: 400,
13
+ scales: {
14
+ preview: {
15
+ download:
16
+ 'http://localhost:3000/image/@@images/image/image-400.png',
17
+ width: 400,
18
+ height: 400,
19
+ },
20
+ },
21
+ },
22
+ }}
23
+ imageField="image"
24
+ alt="alt text"
25
+ />,
26
+ );
27
+ const json = component.toJSON();
28
+ expect(json).toMatchSnapshot();
29
+ });
30
+
31
+ test('renders an image component with lazy loading', () => {
32
+ const component = renderer.create(
33
+ <Image
34
+ item={{
35
+ image: {
36
+ download: 'http://localhost:3000/image/@@images/image/image.png',
37
+ width: 400,
38
+ height: 400,
39
+ scales: {
40
+ preview: {
41
+ download:
42
+ 'http://localhost:3000/image/@@images/image/image-400.png',
43
+ width: 400,
44
+ height: 400,
45
+ },
46
+ },
47
+ },
48
+ }}
49
+ imageField="image"
50
+ alt="alt text"
51
+ loading="lazy"
52
+ />,
53
+ );
54
+ const json = component.toJSON();
55
+ expect(json).toMatchSnapshot();
56
+ });
57
+
58
+ test('renders an image component with responsive class', () => {
59
+ const component = renderer.create(
60
+ <Image
61
+ item={{
62
+ image: {
63
+ download: 'http://localhost:3000/image/@@images/image/image.png',
64
+ width: 400,
65
+ height: 400,
66
+ scales: {
67
+ preview: {
68
+ download:
69
+ 'http://localhost:3000/image/@@images/image/image-400.png',
70
+ width: 400,
71
+ height: 400,
72
+ },
73
+ },
74
+ },
75
+ }}
76
+ imageField="image"
77
+ alt="alt text"
78
+ responsive={true}
79
+ />,
80
+ );
81
+ const json = component.toJSON();
82
+ expect(json).toMatchSnapshot();
83
+ });
84
+
85
+ test('renders an image component from a catalog brain', () => {
86
+ const component = renderer.create(
87
+ <Image
88
+ item={{
89
+ '@id': 'http://localhost:3000/image',
90
+ image_field: 'image',
91
+ image_scales: {
92
+ image: [
93
+ {
94
+ download: '@@images/image/image.png',
95
+ width: 400,
96
+ height: 400,
97
+ scales: {
98
+ preview: {
99
+ download: '@@images/image/image-400.png',
100
+ width: 400,
101
+ height: 400,
102
+ },
103
+ },
104
+ },
105
+ ],
106
+ },
107
+ }}
108
+ imageField="image"
109
+ alt="alt text"
110
+ />,
111
+ );
112
+ const json = component.toJSON();
113
+ expect(json).toMatchSnapshot();
114
+ });
115
+
116
+ test('renders an image component from a string src', () => {
117
+ const component = renderer.create(
118
+ <Image
119
+ src="http://localhost:3000/image/@@images/image/image.png"
120
+ alt="alt text"
121
+ />,
122
+ );
123
+ const json = component.toJSON();
124
+ expect(json).toMatchSnapshot();
125
+ });
@@ -42,6 +42,8 @@ const Logo = () => {
42
42
  src={LogoImage}
43
43
  alt={intl.formatMessage(messages.plonesite)}
44
44
  title={intl.formatMessage(messages.plonesite)}
45
+ width="158.2"
46
+ height="40.7"
45
47
  />
46
48
  </UniversalLink>
47
49
  );