@plone/volto 19.0.0-alpha.21 → 19.0.0-alpha.23

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,31 @@ myst:
17
17
 
18
18
  <!-- towncrier release notes start -->
19
19
 
20
+ ## 19.0.0-alpha.23 (2026-02-03)
21
+
22
+ ### Bugfix
23
+
24
+ - Language control panel: fix validation of default language. @davisagli [#7720](https://github.com/plone/volto/issues/7720)
25
+ - Set HTTP 503 status code for ConnectionRefused error page. @Shyam-Raghuwanshi [#7754](https://github.com/plone/volto/issues/7754)
26
+ - Fix default case selection in ButtonsWidget. @iFlameing
27
+
28
+ ## 19.0.0-alpha.22 (2026-01-26)
29
+
30
+ ### Feature
31
+
32
+ - Add `aboveListingItems` slot to allow displaying custom listing disclamers. @danalvrz
33
+ - Enhanced `SlotRenderer`'s `GetSlotArgs` typings. @sneridagh
34
+
35
+ ### Bugfix
36
+
37
+ - Remove '__ac' cookie when authtoken expires @Tishasoumya-02 [#7783](https://github.com/plone/volto/issues/7783)
38
+
39
+ ### Internal
40
+
41
+ - Removed apps folder from main branch, and related Plone's modular architecture packages (helpers, blocks, providers, theming, layout).
42
+ These packages are now only in `seven` branch. @sneridagh [#7785](https://github.com/plone/volto/issues/7785)
43
+ - Fixed CI test call. Fixed missing tests fixed since then. @sneridagh [#7795](https://github.com/plone/volto/issues/7795)
44
+
20
45
  ## 19.0.0-alpha.21 (2026-01-14)
21
46
 
22
47
  ### Bugfix
package/README.md CHANGED
@@ -47,8 +47,6 @@ This allows the code to be shared effectively, and unifies tracking of changes a
47
47
  |---|---|
48
48
  | [`@plone/client`](https://www.npmjs.com/package/@plone/client) | [`packages/client`](https://github.com/plone/volto/tree/main/packages/client#readme) |
49
49
  | [`@plone/components`](https://www.npmjs.com/package/@plone/components) | [`packages/components`](https://github.com/plone/volto/tree/main/packages/components#readme) |
50
- | [`@plone/helpers`](https://www.npmjs.com/package/@plone/helpers) | [`packages/helpers`](https://github.com/plone/volto/tree/main/packages/helpers#readme) |
51
- | [`@plone/providers`](https://www.npmjs.com/package/@plone/providers) | [`packages/providers`](https://github.com/plone/volto/tree/main/packages/providers#readme) |
52
50
  | [`@plone/registry`](https://www.npmjs.com/package/@plone/registry) | [`packages/registry`](https://github.com/plone/volto/tree/main/packages/registry#readme) |
53
51
  | [`@plone/scripts`](https://www.npmjs.com/package/@plone/scripts) | [`packages/scripts`](https://github.com/plone/volto/tree/main/packages/scripts#readme) |
54
52
  | [`@plone/types`](https://www.npmjs.com/package/@plone/types) | [`packages/types`](https://github.com/plone/volto/tree/main/packages/types#readme) |
@@ -140,7 +138,6 @@ To ensure your website gets the greatest exposure, add it both to [Awesome Volto
140
138
  - [Debabarreneko mankomunitatea](https://debabarrena.eus/eu) (Website of the Commonwealth of Debabarrena, community of municipalities to centralize waste handling services, developed by [CodeSyntax](https://www.codesyntax.com/en), 2022)
141
139
  - [Debako Udala / Ayuntamiento de Deba](https://www.deba.eus/eu) (Website of the municipality of Deba, developed by [CodeSyntax](https://www.codesyntax.com/en), 2022)
142
140
  - [European Environment Agency](https://www.eea.europa.eu/en) (Website of the European Environment Agency. Developed by [Eau de Web](https://eaudeweb.ro), 2023)
143
- - Excellence at Humboldt-Universität zu Berlin (Website for the excellence initiative of the [Humboldt University Berlin](https://www.hu-berlin.de/de), developed by [kitconcept GmbH](https://kitconcept.com/en), 2019)
144
141
  - [Film Basque Country](https://www.filmbasquecountry.eus/en) (Website to attract, guide, and support international productions, making it easier for them to film in the Basque Country, developed by [CodeSyntax](https://www.codesyntax.com/en), 2025)
145
142
  - [Forest Information System for Europe](https://forest.eea.europa.eu) (Thematic website focusing on European forests, developed by [Eau de Web](https://eaudeweb.ro/), 2019)
146
143
  - [Forschungszentrum Jülich](https://www.fz-juelich.de/de) (Website for Forschungzentrum Jülich, which is one of the largest research institutions in Europe, developed by [kitconcept GmbH](https://kitconcept.com/en), 2022)
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "19.0.0-alpha.21",
12
+ "version": "19.0.0-alpha.23",
13
13
  "repository": {
14
14
  "type": "git",
15
15
  "url": "git@github.com:plone/volto.git"
@@ -191,9 +191,9 @@
191
191
  "use-deep-compare-effect": "1.8.1",
192
192
  "uuid": "^8.3.2",
193
193
  "@plone/components": "4.0.0-alpha.4",
194
- "@plone/registry": "3.0.0-alpha.9",
194
+ "@plone/scripts": "4.0.0-alpha.4",
195
195
  "@plone/volto-slate": "19.0.0-alpha.9",
196
- "@plone/scripts": "4.0.0-alpha.4"
196
+ "@plone/registry": "3.0.0-alpha.9"
197
197
  },
198
198
  "devDependencies": {
199
199
  "@babel/core": "^7.28.5",
@@ -214,6 +214,7 @@
214
214
  "@storybook/addon-webpack5-compiler-babel": "3.0.3",
215
215
  "@storybook/react": "^8.0.4",
216
216
  "@storybook/react-webpack5": "^8.0.4",
217
+ "@storybook/test": "^8.0.4",
217
218
  "@storybook/theming": "^8.0.4",
218
219
  "@testing-library/cypress": "10.1.0",
219
220
  "@testing-library/jest-dom": "6.4.2",
@@ -306,10 +307,10 @@
306
307
  "webpack-bundle-analyzer": "4.10.1",
307
308
  "webpack-dev-server": "4.11.1",
308
309
  "webpack-node-externals": "3.0.0",
309
- "@plone/babel-preset-razzle": "^1.0.0-alpha.0",
310
+ "@plone/types": "2.0.0-alpha.14",
310
311
  "@plone/razzle": "1.0.0-alpha.0",
311
- "@plone/volto-coresandbox": "1.0.0",
312
- "@plone/types": "2.0.0-alpha.12"
312
+ "@plone/babel-preset-razzle": "^1.0.0-alpha.0",
313
+ "@plone/volto-coresandbox": "1.0.0"
313
314
  },
314
315
  "volta": {
315
316
  "node": "20.9.0"
@@ -264,9 +264,11 @@ const BlocksForm = (props) => {
264
264
  // to be removed when the user saves the page next. Otherwise the invalid
265
265
  // blocks would linger for ever.
266
266
 
267
- for (const [n, v] of blockList) {
268
- if (!v) {
269
- const newFormData = deleteBlock(properties, n, intl);
267
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(properties);
268
+ const blocksFieldname = getBlocksFieldname(properties);
269
+ for (const id of properties?.[blocksLayoutFieldname]?.items || []) {
270
+ if (!properties?.[blocksFieldname]?.[id]) {
271
+ const newFormData = deleteBlock(properties, id, intl);
270
272
  onChangeFormData(newFormData);
271
273
  }
272
274
  }
@@ -12,6 +12,8 @@ import { normalizeString } from '@plone/volto/helpers/Utils/Utils';
12
12
  import paginationLeftSVG from '@plone/volto/icons/left-key.svg';
13
13
  import paginationRightSVG from '@plone/volto/icons/right-key.svg';
14
14
 
15
+ import SlotRenderer from '@plone/volto/components/theme/SlotRenderer/SlotRenderer';
16
+
15
17
  const Headline = ({ headlineTag, id, data = {}, listingItems, isEditMode }) => {
16
18
  let attr = { id };
17
19
  const slug = Slugger.slug(normalizeString(data.headline));
@@ -46,6 +48,7 @@ const ListingBody = withQuerystringResults((props) => {
46
48
  isFolderContentsListing,
47
49
  hasLoaded,
48
50
  id,
51
+ content,
49
52
  } = props;
50
53
 
51
54
  let ListingBodyTemplate;
@@ -82,6 +85,7 @@ const ListingBody = withQuerystringResults((props) => {
82
85
  isEditMode={isEditMode}
83
86
  />
84
87
  )}
88
+ <SlotRenderer name="aboveListingItems" content={content} data={data} />
85
89
  {listingItems?.length > 0 ? (
86
90
  <div ref={listingRef}>
87
91
  <ListingBodyTemplate
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useEffect } from 'react';
2
2
  import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
3
3
  import Icon from '@plone/volto/components/theme/Icon/Icon';
4
4
  import { Radio, RadioGroup } from '@plone/components';
@@ -130,6 +130,25 @@ const ButtonsWidget = (props: ButtonsWidgetProps) => {
130
130
  }
131
131
  };
132
132
 
133
+ // Synchronize default value if no value is set to data prop.
134
+ useEffect(() => {
135
+ // If `value` already matches any normalized action value, do nothing.
136
+ const existingMatch = normalizedActions.find(({ value: actionValue }) =>
137
+ isEqual(value, actionValue),
138
+ );
139
+
140
+ if (existingMatch) return;
141
+
142
+ // Otherwise, if there's a default value set the default style.
143
+ if (!defaultSelectedActionName) return;
144
+ const selected = normalizedActions.find(
145
+ ({ name }) => name === defaultSelectedActionName,
146
+ );
147
+ if (!selected) return;
148
+
149
+ onChange(id, selected.value);
150
+ }, [value, defaultSelectedActionName, onChange, normalizedActions, id]);
151
+
133
152
  return (
134
153
  <FormFieldWrapper {...props} className="widget">
135
154
  <RadioGroup
@@ -176,7 +176,9 @@ export class App extends Component {
176
176
  <main ref={this.mainRef}>
177
177
  <OutdatedBrowser />
178
178
  {this.props.connectionRefused ? (
179
- <ConnectionRefusedView />
179
+ <ConnectionRefusedView
180
+ staticContext={this.props.staticContext}
181
+ />
180
182
  ) : this.state.hasError ? (
181
183
  <Error
182
184
  message={this.state.error.message}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Home container.
2
+ * Connection refused error page.
3
3
  * @module components/theme/ConnectionRefused/ConnectionRefused
4
4
  */
5
5
 
@@ -7,6 +7,7 @@ import React from 'react';
7
7
  import { FormattedMessage } from 'react-intl';
8
8
  import { Container } from 'semantic-ui-react';
9
9
  import config from '@plone/volto/registry';
10
+ import { withServerErrorCode } from '@plone/volto/helpers/Utils/Utils';
10
11
 
11
12
  const ConnectionRefused = () => (
12
13
  <Container
@@ -71,4 +72,4 @@ const ConnectionRefused = () => (
71
72
  </Container>
72
73
  );
73
74
 
74
- export default ConnectionRefused;
75
+ export default withServerErrorCode(503)(ConnectionRefused);
@@ -1,6 +1,10 @@
1
1
  import { useLocation } from 'react-router-dom';
2
2
  import config from '@plone/volto/registry';
3
- import type { Content } from '@plone/types';
3
+ import type { GetSlotArgs } from '@plone/types';
4
+
5
+ export interface SlotRendererProps extends GetSlotArgs {
6
+ name: string;
7
+ }
4
8
 
5
9
  /*
6
10
  Usage:
@@ -11,20 +15,20 @@ const SlotRenderer = ({
11
15
  name,
12
16
  content,
13
17
  navRoot,
14
- }: {
15
- name: string;
16
- content: Content;
17
- navRoot?: Content;
18
- }) => {
18
+ data,
19
+ ...rest
20
+ }: SlotRendererProps) => {
19
21
  const location = useLocation();
20
22
 
21
23
  let slots = config.getSlot(name, {
24
+ ...rest,
22
25
  content,
23
26
  location: location as any, // Since we are using an older version of history, we need to cast it to any
24
27
  // This is to cover the use case while adding a new content and we don't have
25
28
  // have the navRoot information in the initial content. This will be
26
29
  // useful for SlotRenderers rendered in the `Add` route.
27
30
  navRoot: content?.['@components']?.navroot?.navroot || navRoot,
31
+ data,
28
32
  });
29
33
 
30
34
  if (!slots) {
@@ -45,10 +49,12 @@ const SlotRenderer = ({
45
49
  const SlotComponent = component;
46
50
  return (
47
51
  <SlotComponent
52
+ {...rest}
48
53
  key={name}
49
54
  content={content}
50
55
  location={location}
51
56
  navRoot={navRoot}
57
+ data={data}
52
58
  />
53
59
  );
54
60
  },
@@ -5,7 +5,6 @@
5
5
 
6
6
  import Cookies from 'universal-cookie';
7
7
  import jwtDecode from 'jwt-decode';
8
-
9
8
  import { loginRenew } from '@plone/volto/actions/userSession/userSession';
10
9
  import { getCookieOptions } from '@plone/volto/helpers/Cookies/cookies';
11
10
  import { push } from 'connected-react-router';
@@ -35,7 +34,6 @@ export function persistAuthToken(store, req) {
35
34
  } else {
36
35
  currentValue = cookies.get('auth_token');
37
36
  }
38
-
39
37
  /**
40
38
  * handleChange method.
41
39
  * @method handleChange
@@ -46,7 +44,6 @@ export function persistAuthToken(store, req) {
46
44
  const previousValue = currentValue;
47
45
  const state = store.getState();
48
46
  currentValue = state.userSession.token;
49
-
50
47
  if (
51
48
  module.hot &&
52
49
  module.hot.data &&
@@ -55,11 +52,11 @@ export function persistAuthToken(store, req) {
55
52
  ) {
56
53
  currentValue = previousValue;
57
54
  }
58
-
59
55
  if (previousValue !== currentValue || initial) {
60
56
  if (!currentValue) {
61
57
  if (previousValue) {
62
58
  cookies.remove('auth_token', { path: '/' });
59
+ cookies.remove('__ac', { path: '/' });
63
60
  }
64
61
  } else {
65
62
  if (previousValue !== currentValue) {
@@ -97,11 +94,9 @@ export function persistAuthToken(store, req) {
97
94
  }
98
95
  }
99
96
  }
100
-
101
97
  store.subscribe(handleChange);
102
98
  handleChange(true);
103
99
  }
104
-
105
100
  if (module?.hot) {
106
101
  module.hot.dispose((data) => {
107
102
  data.reloaded = true;
@@ -15,6 +15,17 @@ type Validator = {
15
15
  formatMessage: Function;
16
16
  };
17
17
 
18
+ type Choice = {
19
+ token: string;
20
+ label: string;
21
+ };
22
+ type ChoiceValidator = {
23
+ value: string | Choice;
24
+ field: Record<string, any>;
25
+ formData: any;
26
+ formatMessage: Function;
27
+ };
28
+
18
29
  export const isMaxPropertyValid = ({
19
30
  value,
20
31
  fieldSpec,
@@ -211,12 +222,13 @@ export const defaultLanguageControlPanelValidator = ({
211
222
  value,
212
223
  formData,
213
224
  formatMessage,
214
- }: Validator) => {
225
+ }: ChoiceValidator) => {
226
+ const token = typeof value === 'object' ? value.token : value;
215
227
  const isValid =
216
- value &&
228
+ token &&
217
229
  (formData.available_languages.find(
218
- (lang: { token: string }) => lang.token === value,
230
+ (lang: { token: string }) => lang.token === token,
219
231
  ) ||
220
- formData.available_languages.includes(value));
232
+ formData.available_languages.includes(token));
221
233
  return !isValid ? formatMessage(messages.defaultLanguage) : null;
222
234
  };
@@ -1,2 +1,2 @@
1
- export default ConnectionRefused;
2
- declare function ConnectionRefused(): import("react/jsx-runtime").JSX.Element;
1
+ declare const _default: (props: any) => import("react/jsx-runtime").JSX.Element;
2
+ export default _default;
@@ -1,7 +1,6 @@
1
- import type { Content } from '@plone/types';
2
- declare const SlotRenderer: ({ name, content, navRoot, }: {
1
+ import type { GetSlotArgs } from '@plone/types';
2
+ export interface SlotRendererProps extends GetSlotArgs {
3
3
  name: string;
4
- content: Content;
5
- navRoot?: Content;
6
- }) => import("react/jsx-runtime").JSX.Element;
4
+ }
5
+ declare const SlotRenderer: ({ name, content, navRoot, data, ...rest }: SlotRendererProps) => import("react/jsx-runtime").JSX.Element;
7
6
  export default SlotRenderer;
@@ -42,7 +42,7 @@ export const errorViews: {
42
42
  403: (props: any) => import("react/jsx-runtime").JSX.Element;
43
43
  408: () => string;
44
44
  500: (props: any) => import("react/jsx-runtime").JSX.Element;
45
- ECONNREFUSED: () => import("react/jsx-runtime").JSX.Element;
45
+ ECONNREFUSED: (props: any) => import("react/jsx-runtime").JSX.Element;
46
46
  corsError: () => string;
47
47
  };
48
48
  export namespace layoutViewsNamesMapping {
@@ -10,6 +10,16 @@ type Validator = {
10
10
  formData: any;
11
11
  formatMessage: Function;
12
12
  };
13
+ type Choice = {
14
+ token: string;
15
+ label: string;
16
+ };
17
+ type ChoiceValidator = {
18
+ value: string | Choice;
19
+ field: Record<string, any>;
20
+ formData: any;
21
+ formatMessage: Function;
22
+ };
13
23
  export declare const isMaxPropertyValid: ({ value, fieldSpec, criterion, formatMessage, }: MinMaxValidator) => any;
14
24
  export declare const isMinPropertyValid: ({ value, fieldSpec, criterion, formatMessage, }: MinMaxValidator) => any;
15
25
  export declare const minLengthValidator: ({ value, field, formatMessage, }: Validator) => any;
@@ -26,5 +36,5 @@ export declare const endEventDateRangeValidator: ({ value, field, formData, form
26
36
  export declare const patternValidator: ({ value, field, formatMessage, }: Validator) => any;
27
37
  export declare const maxItemsValidator: ({ value, field, formatMessage, }: Validator) => any;
28
38
  export declare const minItemsValidator: ({ value, field, formatMessage, }: Validator) => any;
29
- export declare const defaultLanguageControlPanelValidator: ({ value, formData, formatMessage, }: Validator) => any;
39
+ export declare const defaultLanguageControlPanelValidator: ({ value, formData, formatMessage, }: ChoiceValidator) => any;
30
40
  export {};