@plone/volto 18.15.1 → 18.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -17,6 +17,23 @@ myst:
17
17
 
18
18
  <!-- towncrier release notes start -->
19
19
 
20
+ ## 18.17.0 (2025-05-06)
21
+
22
+ ### Feature
23
+
24
+ - Added `contained` class if `isContainer` prop is passed (from containers). @sneridagh [#7043](https://github.com/plone/volto/issues/7043)
25
+
26
+ ## 18.16.0 (2025-05-01)
27
+
28
+ ### Feature
29
+
30
+ - Rename `vitest.config.ts` to `vitest.config.mjs` for allowing it to be loaded from add-ons. @sneridagh [#7035](https://github.com/plone/volto/issues/7035)
31
+
32
+ ### Bugfix
33
+
34
+ - Added missing dependency for `sgvo`. Fixed `svgo` library loading in `./vite-plugins/svg.js`. @sneridagh [#7035](https://github.com/plone/volto/issues/7035)
35
+ - Rename `./vite-plugins/svg.js` to proper extension `vite-plugins/svg.mjs`. @sneridagh [#7035](https://github.com/plone/volto/issues/7035)
36
+
20
37
  ## 18.15.1 (2025-04-30)
21
38
 
22
39
  ### Internal
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "18.15.1",
12
+ "version": "18.17.0",
13
13
  "repository": {
14
14
  "type": "git",
15
15
  "url": "git@github.com:plone/volto.git"
@@ -237,8 +237,8 @@
237
237
  "use-deep-compare-effect": "1.8.1",
238
238
  "uuid": "^8.3.2",
239
239
  "@plone/registry": "2.5.3",
240
- "@plone/scripts": "3.9.0",
241
- "@plone/volto-slate": "18.3.0"
240
+ "@plone/volto-slate": "18.3.0",
241
+ "@plone/scripts": "3.9.0"
242
242
  },
243
243
  "devDependencies": {
244
244
  "@babel/core": "^7.0.0",
@@ -349,6 +349,7 @@
349
349
  "stylelint-config-idiomatic-order": "10.0.0",
350
350
  "stylelint-prettier": "5.0.0",
351
351
  "svg-loader": "0.0.2",
352
+ "svgo": "^3.0.0",
352
353
  "svgo-loader": "3.0.3",
353
354
  "terser-webpack-plugin": "5.3.6",
354
355
  "tmp": "0.2.1",
@@ -359,6 +359,7 @@ const BlocksForm = (props) => {
359
359
  editable,
360
360
  showBlockChooser: selectedBlock === childId,
361
361
  detached: isContainer,
362
+ isContainer,
362
363
  // Properties to pass to the BlocksForm to match the View ones
363
364
  content: properties,
364
365
  history,
@@ -122,7 +122,7 @@ export class Edit extends Component {
122
122
  */
123
123
  render() {
124
124
  const { blocksConfig = config.blocks.blocksConfig } = this.props;
125
- const { editable, type } = this.props;
125
+ const { editable, type, isContainer: parentIsContainer } = this.props;
126
126
 
127
127
  const disableNewBlocks = this.props.data?.disableNewBlocks;
128
128
 
@@ -198,6 +198,7 @@ export class Edit extends Component {
198
198
  {...this.props}
199
199
  blockNode={this.blockNode}
200
200
  data={this.props.data}
201
+ className={cx({ contained: parentIsContainer })}
201
202
  />
202
203
  {this.props.manage && (
203
204
  <SidebarPortal
@@ -9,7 +9,13 @@ import {
9
9
  const StyleWrapper = (props) => {
10
10
  let classNames,
11
11
  style = [];
12
- const { block, children, content, data = {}, isContainer } = props;
12
+ const {
13
+ block,
14
+ children,
15
+ content,
16
+ data = {},
17
+ isContainer: parentIsContainer,
18
+ } = props;
13
19
  classNames = buildStyleClassNamesFromData(data.styles);
14
20
 
15
21
  classNames = buildStyleClassNamesExtenders({
@@ -24,14 +30,16 @@ const StyleWrapper = (props) => {
24
30
  '',
25
31
  // If we are rendering blocks inside a container, then pass also the data from the container
26
32
  // This is needed in order to calculate properly the styles for the blocks inside the container
27
- isContainer && content.blocks ? content : {},
33
+ parentIsContainer && content.blocks ? content : {},
28
34
  );
29
35
 
30
36
  const rewrittenChildren = React.Children.map(children, (child) => {
31
37
  if (React.isValidElement(child)) {
32
38
  const childProps = {
33
39
  ...props,
34
- className: cx([child.props.className, ...classNames]),
40
+ className: cx([child.props.className, ...classNames], {
41
+ contained: parentIsContainer,
42
+ }),
35
43
  style: { ...child.props.style, ...style },
36
44
  };
37
45
  return React.cloneElement(child, childProps);
@@ -52,7 +52,7 @@ export function svgLoader(options = {}) {
52
52
 
53
53
  const match = content.match(/<svg([^>]+)+>([\s\S]+)<\/svg>/i);
54
54
  let attrs = {};
55
- // console.log(content);
55
+
56
56
  if (match) {
57
57
  attrs = match[1];
58
58
  if (attrs) {
@@ -1,7 +1,16 @@
1
1
  import { defineConfig } from 'vitest/config';
2
2
  import react from '@vitejs/plugin-react';
3
3
  import path from 'path';
4
- import { svgLoader } from './vite-plugins/svg.js';
4
+ import { svgLoader } from './vite-plugins/svg.mjs';
5
+ import { fileURLToPath } from 'url';
6
+ import { createRequire } from 'module';
7
+
8
+ const require = createRequire(import.meta.url);
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+
13
+ const projectRoot = path.resolve(__dirname);
5
14
 
6
15
  export default defineConfig({
7
16
  plugins: [
@@ -10,12 +19,19 @@ export default defineConfig({
10
19
  svgoConfig: {
11
20
  plugins: [
12
21
  {
13
- name: 'removeViewBox',
14
- active: false,
22
+ name: 'preset-default',
23
+ params: {
24
+ overrides: {
25
+ convertPathData: false,
26
+ removeViewBox: false,
27
+ },
28
+ },
15
29
  },
30
+ 'removeTitle',
31
+ 'removeUselessStrokeAndFill',
16
32
  ],
17
33
  },
18
- }) as any,
34
+ }),
19
35
  ],
20
36
  resolve: {
21
37
  alias: {
@@ -36,12 +52,12 @@ export default defineConfig({
36
52
  globals: true,
37
53
  environment: 'jsdom',
38
54
  setupFiles: [
39
- './test-setup-globals-vitest.js',
40
- './test-setup-config.jsx',
41
- './jest-setup-afterenv.js',
42
- './jest-addons-loader.js',
55
+ `${projectRoot}/test-setup-globals-vitest.js`,
56
+ `${projectRoot}/test-setup-config.jsx`,
57
+ `${projectRoot}/jest-setup-afterenv.js`,
58
+ `${projectRoot}/jest-addons-loader.js`,
43
59
  ],
44
- globalSetup: './global-test-setup.js',
60
+ globalSetup: `${projectRoot}/global-test-setup.js`,
45
61
  coverage: {
46
62
  provider: 'v8',
47
63
  reporter: ['text', 'json', 'html'],
@@ -1,123 +0,0 @@
1
- import React from 'react';
2
- import { Provider } from 'react-intl-redux';
3
- import { render, waitFor } from '@testing-library/react';
4
- import configureStore from 'redux-mock-store';
5
- import FileWidget from './FileWidget';
6
-
7
- vi.spyOn(global.Date, 'now').mockImplementation(() => 1234567890);
8
-
9
- const mockStore = configureStore();
10
-
11
- const createStore = () =>
12
- mockStore({
13
- intl: {
14
- locale: 'en',
15
- messages: {},
16
- },
17
- });
18
-
19
- describe('FileWidget', () => {
20
- beforeEach(() => {
21
- vi.clearAllTimers();
22
- Object.defineProperty(global.Image.prototype, 'complete', {
23
- get() {
24
- return true;
25
- },
26
- });
27
- });
28
-
29
- afterEach(() => {
30
- vi.clearAllMocks();
31
- });
32
-
33
- test('renders an empty file widget component', async () => {
34
- const store = createStore();
35
-
36
- const { container } = render(
37
- <Provider store={store}>
38
- <FileWidget
39
- id="my-field"
40
- title="My field"
41
- fieldSet="default"
42
- onChange={() => {}}
43
- />
44
- </Provider>,
45
- );
46
-
47
- await waitFor(
48
- () => {
49
- expect(container.querySelector('.file-widget-dropzone')).toBeTruthy();
50
- },
51
- { timeout: 1000 },
52
- );
53
-
54
- expect(container).toMatchSnapshot();
55
- });
56
-
57
- test('renders a file widget component with value', async () => {
58
- const store = createStore();
59
-
60
- const { container } = render(
61
- <Provider store={store}>
62
- <FileWidget
63
- id="my-field"
64
- title="My field"
65
- fieldSet="default"
66
- onChange={() => {}}
67
- value={{
68
- download: 'http://myfile',
69
- 'content-type': 'image/png',
70
- filename: 'myfile',
71
- encoding: '',
72
- }}
73
- />
74
- </Provider>,
75
- );
76
-
77
- await waitFor(
78
- () => {
79
- const dropzone = container.querySelector('.file-widget-dropzone');
80
- const preview = container.querySelector('.image-preview');
81
- const filename = container.querySelector('.field-file-name');
82
-
83
- return dropzone && preview && filename;
84
- },
85
- { timeout: 1000 },
86
- );
87
-
88
- expect(container).toMatchSnapshot();
89
- });
90
-
91
- test('renders a file widget component with value in raw data', async () => {
92
- const store = createStore();
93
-
94
- const { container } = render(
95
- <Provider store={store}>
96
- <FileWidget
97
- id="my-field"
98
- title="My field"
99
- fieldSet="default"
100
- onChange={() => {}}
101
- value={{
102
- data: 'oiweurtksdgfjaslfqw9523563456',
103
- 'content-type': 'image/png',
104
- filename: 'myfile',
105
- encoding: 'base64',
106
- }}
107
- />
108
- </Provider>,
109
- );
110
-
111
- await waitFor(
112
- () => {
113
- const dropzone = container.querySelector('.file-widget-dropzone');
114
- const filename = container.querySelector('.field-file-name');
115
-
116
- return dropzone && filename;
117
- },
118
- { timeout: 1000 },
119
- );
120
-
121
- expect(container).toMatchSnapshot();
122
- });
123
- });