@plone/volto 18.30.1 → 18.31.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/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "18.30.1",
12
+ "version": "18.31.0",
13
13
  "repository": {
14
14
  "type": "git",
15
15
  "url": "git@github.com:plone/volto.git"
@@ -241,9 +241,9 @@
241
241
  "url": "^0.11.3",
242
242
  "use-deep-compare-effect": "1.8.1",
243
243
  "uuid": "^8.3.2",
244
+ "@plone/volto-slate": "18.8.0",
244
245
  "@plone/registry": "2.6.0",
245
- "@plone/scripts": "3.10.3",
246
- "@plone/volto-slate": "18.8.0"
246
+ "@plone/scripts": "3.10.3"
247
247
  },
248
248
  "devDependencies": {
249
249
  "@babel/core": "^7.0.0",
@@ -245,15 +245,17 @@ class ModalForm extends Component {
245
245
  const { schema, onCancel, description } = this.props;
246
246
  const currentFieldset = schema.fieldsets[this.state.currentTab];
247
247
 
248
- const fields = map(currentFieldset.fields, (field) => ({
249
- ...schema.properties[field],
250
- id: field,
251
- value: this.state.formData[field],
252
- required: schema.required.indexOf(field) !== -1,
253
- onChange: this.onChangeField,
254
- onBlur: this.onBlurField,
255
- onClick: this.onClickInput,
256
- }));
248
+ const fields = currentFieldset
249
+ ? map(currentFieldset.fields, (field) => ({
250
+ ...schema.properties[field],
251
+ id: field,
252
+ value: this.state.formData[field],
253
+ required: schema.required.indexOf(field) !== -1,
254
+ onChange: this.onChangeField,
255
+ onBlur: this.onBlurField,
256
+ onClick: this.onClickInput,
257
+ }))
258
+ : [];
257
259
 
258
260
  const state_errors = keys(this.state.errors).length > 0;
259
261
  return (
@@ -288,7 +290,7 @@ class ModalForm extends Component {
288
290
  )}
289
291
  <div>{this.props.submitError}</div>
290
292
  </Message>
291
- {schema.fieldsets.length > 1 && (
293
+ {schema.fieldsets?.length > 1 && (
292
294
  <Menu tabular stackable>
293
295
  {map(schema.fieldsets, (item, index) => (
294
296
  <Menu.Item
@@ -87,10 +87,35 @@ describe('ModalForm', () => {
87
87
  </Provider>
88
88
  );
89
89
  const { getByText } = render(jsx, {
90
- contaner: document.body,
90
+ container: document.body,
91
91
  });
92
92
 
93
93
  const loadingMessage = getByText(/renaming items.../i);
94
94
  expect(loadingMessage).toBeInTheDocument();
95
95
  });
96
+ it('renders with empty fieldsets array', () => {
97
+ const store = mockStore({
98
+ intl: {
99
+ locale: 'en',
100
+ messages: {},
101
+ },
102
+ });
103
+ const component = renderer.create(
104
+ <Provider store={store}>
105
+ <ModalForm
106
+ schema={{
107
+ fieldsets: [],
108
+ properties: {},
109
+ required: [],
110
+ }}
111
+ onSubmit={() => {}}
112
+ onCancel={() => {}}
113
+ open={false}
114
+ title="Action without form"
115
+ />
116
+ </Provider>,
117
+ );
118
+ const json = component.toJSON();
119
+ expect(json).toMatchSnapshot();
120
+ });
96
121
  });
@@ -84,6 +84,7 @@ const DatetimeWidgetComponent = (props) => {
84
84
  widget,
85
85
  noPastDates: propNoPastDates,
86
86
  isDisabled,
87
+ formData,
87
88
  } = props;
88
89
 
89
90
  const intl = useIntl();
@@ -106,12 +107,21 @@ const DatetimeWidgetComponent = (props) => {
106
107
  );
107
108
  }, [value, lang, moment]);
108
109
 
110
+ // If open_end is checked and this is the end field, don't render
111
+ if (id === 'end' && formData?.open_end) {
112
+ return null;
113
+ }
114
+
109
115
  const getInternalValue = () => {
110
116
  return parseDateTime(toBackendLang(lang), value, undefined, moment.default);
111
117
  };
112
118
 
113
119
  const getDateOnly = () => {
114
- return dateOnly || widget === 'date';
120
+ return (
121
+ dateOnly ||
122
+ widget === 'date' ||
123
+ ((id === 'start' || id === 'end') && formData?.whole_day)
124
+ );
115
125
  };
116
126
 
117
127
  const onDateChange = (date) => {
@@ -2,12 +2,12 @@
2
2
  * FormFieldWrapper component.
3
3
  * @module components/manage/Widgets/FormFieldWrapper
4
4
  */
5
- import React, { Component } from 'react';
5
+ import React from 'react';
6
6
  import PropTypes from 'prop-types';
7
7
  import { Form, Grid, Icon as IconOld, Label } from 'semantic-ui-react';
8
8
  import map from 'lodash/map';
9
9
  import cx from 'classnames';
10
- import { defineMessages, injectIntl } from 'react-intl';
10
+ import { defineMessages, useIntl } from 'react-intl';
11
11
  import LanguageSVG from '@plone/volto/icons/language.svg';
12
12
  import Icon from '@plone/volto/components/theme/Icon/Icon';
13
13
 
@@ -31,178 +31,156 @@ const messages = defineMessages({
31
31
  },
32
32
  });
33
33
  /**
34
- * FormFieldWrapper component class.
35
- * @class FormFieldWrapper
36
- * @extends Component
34
+ * FormFieldWrapper component.
35
+ * @function FormFieldWrapper
36
+ * @param {Object} props - Component props
37
+ * @returns {JSX.Element} Markup for the component.
37
38
  */
38
- class FormFieldWrapper extends Component {
39
- /**
40
- * Property types.
41
- * @property {Object} propTypes Property types.
42
- * @static
43
- */
44
- static propTypes = {
45
- id: PropTypes.string.isRequired,
46
- title: PropTypes.string.isRequired,
47
- description: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
48
- required: PropTypes.bool,
49
- error: PropTypes.arrayOf(PropTypes.string),
50
- wrapped: PropTypes.bool,
51
- columns: PropTypes.number,
52
- draggable: PropTypes.bool,
53
- isDisabled: PropTypes.bool,
54
- onEdit: PropTypes.func,
55
- className: PropTypes.string,
56
- onDelete: PropTypes.func,
57
- intl: PropTypes.object,
58
- };
59
-
60
- /**
61
- * Default properties
62
- * @property {Object} defaultProps Default properties.
63
- * @static
64
- */
65
- static defaultProps = {
66
- description: null,
67
- required: false,
68
- error: [],
69
- wrapped: true,
70
- columns: 2,
71
- onDelete: null,
72
- intl: null,
73
- isDisabled: null,
74
- draggable: null,
75
- };
76
-
77
- /**
78
- * Render method.
79
- * @method render
80
- * @returns {string} Markup for the component.
81
- */
82
- render() {
83
- const {
84
- id,
85
- title,
86
- description,
87
- fieldSet,
88
- required,
89
- error,
90
- wrapped,
91
- columns,
92
- draggable,
93
- onEdit,
94
- className,
95
- isDisabled,
96
- onDelete,
97
- intl,
98
- noForInFieldLabel,
99
- multilingual_options,
100
- } = this.props;
101
-
102
- const languageIndependent = multilingual_options?.language_independent;
39
+ const FormFieldWrapper = ({
40
+ id,
41
+ title,
42
+ description = null,
43
+ fieldSet,
44
+ required = false,
45
+ error = [],
46
+ wrapped = true,
47
+ columns = 2,
48
+ draggable = null,
49
+ onEdit,
50
+ className,
51
+ isDisabled = null,
52
+ onDelete = null,
53
+ noForInFieldLabel,
54
+ multilingual_options,
55
+ children,
56
+ }) => {
57
+ const intl = useIntl();
58
+ const languageIndependent = multilingual_options?.language_independent;
103
59
 
104
- const wdg = (
105
- <>
106
- {this.props.children}
60
+ const wdg = (
61
+ <>
62
+ {children}
107
63
 
108
- {map(error, (message) => (
109
- <Label key={message} basic color="red" className="form-error-label">
110
- {message}
111
- </Label>
112
- ))}
113
- </>
114
- );
64
+ {map(error, (message) => (
65
+ <Label key={message} basic color="red" className="form-error-label">
66
+ {message}
67
+ </Label>
68
+ ))}
69
+ </>
70
+ );
115
71
 
116
- return wrapped ? (
117
- <Form.Field
118
- inline
119
- required={required}
120
- error={error && error.length > 0}
121
- className={cx(
122
- description ? 'help' : '',
123
- className,
124
- `field-wrapper-${id}`,
125
- languageIndependent ? 'language-independent-field' : null,
126
- )}
127
- >
128
- <Grid>
129
- <Grid.Row stretched>
130
- {columns === 2 && (
131
- <Grid.Column width="4">
132
- <div className="wrapper">
133
- <label
134
- id={`fieldset-${fieldSet}-field-label-${id}`}
135
- htmlFor={noForInFieldLabel ? null : `field-${id}`}
136
- >
137
- {draggable && onEdit && (
138
- <i
139
- aria-hidden="true"
140
- className="grey bars icon drag handle"
72
+ return wrapped ? (
73
+ <Form.Field
74
+ inline
75
+ required={required}
76
+ error={error && error.length > 0}
77
+ className={cx(
78
+ description ? 'help' : '',
79
+ className,
80
+ `field-wrapper-${id}`,
81
+ languageIndependent ? 'language-independent-field' : null,
82
+ )}
83
+ >
84
+ <Grid>
85
+ <Grid.Row stretched>
86
+ {columns === 2 && (
87
+ <Grid.Column width="4">
88
+ <div className="wrapper">
89
+ <label
90
+ id={`fieldset-${fieldSet}-field-label-${id}`}
91
+ htmlFor={noForInFieldLabel ? null : `field-${id}`}
92
+ >
93
+ {draggable && onEdit && (
94
+ <i
95
+ aria-hidden="true"
96
+ className="grey bars icon drag handle"
97
+ />
98
+ )}
99
+ {title}
100
+ {languageIndependent && (
101
+ <div className="languageIndependent-icon">
102
+ <Icon
103
+ title={intl.formatMessage(
104
+ messages.language_independent_icon_title,
105
+ )}
106
+ name={LanguageSVG}
107
+ size="24px"
108
+ color="#555"
141
109
  />
142
- )}
143
- {title}
144
- {languageIndependent && (
145
- <div className="languageIndependent-icon">
146
- <Icon
147
- title={intl.formatMessage(
148
- messages.language_independent_icon_title,
149
- )}
150
- name={LanguageSVG}
151
- size="24px"
152
- color="#555"
153
- />
154
- </div>
155
- )}
156
- </label>
157
- </div>
158
- </Grid.Column>
110
+ </div>
111
+ )}
112
+ </label>
113
+ </div>
114
+ </Grid.Column>
115
+ )}
116
+ <Grid.Column width={columns === 2 ? 8 : 12}>
117
+ {onEdit && !isDisabled && (
118
+ <div className="toolbar" style={{ zIndex: '2' }}>
119
+ <button
120
+ aria-label={intl.formatMessage(messages.edit)}
121
+ className="item ui noborder button"
122
+ onClick={(evt) => {
123
+ evt.preventDefault();
124
+ onEdit(id);
125
+ }}
126
+ >
127
+ <IconOld name="write square" size="large" color="blue" />
128
+ </button>
129
+ <button
130
+ aria-label={intl.formatMessage(messages.delete)}
131
+ className="item ui noborder button"
132
+ onClick={(evt) => {
133
+ evt.preventDefault();
134
+ onDelete(id);
135
+ }}
136
+ >
137
+ <IconOld name="close" size="large" color="red" />
138
+ </button>
139
+ </div>
159
140
  )}
160
- <Grid.Column width={columns === 2 ? 8 : 12}>
161
- {onEdit && !isDisabled && (
162
- <div className="toolbar" style={{ zIndex: '2' }}>
163
- <button
164
- aria-label={intl.formatMessage(messages.edit)}
165
- className="item ui noborder button"
166
- onClick={(evt) => {
167
- evt.preventDefault();
168
- onEdit(id);
169
- }}
170
- >
171
- <IconOld name="write square" size="large" color="blue" />
172
- </button>
173
- <button
174
- aria-label={intl.formatMessage(messages.delete)}
175
- className="item ui noborder button"
176
- onClick={(evt) => {
177
- evt.preventDefault();
178
- onDelete(id);
179
- }}
180
- >
181
- <IconOld name="close" size="large" color="red" />
182
- </button>
183
- </div>
184
- )}
185
- {wdg}
141
+ {wdg}
142
+ </Grid.Column>
143
+ </Grid.Row>
144
+ {description && (
145
+ <Grid.Row stretched>
146
+ <Grid.Column stretched width="12">
147
+ <p className="help">
148
+ {multilingual_options
149
+ ? `${intl.formatMessage(messages.language_independent)} `
150
+ : null}
151
+ {description}
152
+ </p>
186
153
  </Grid.Column>
187
154
  </Grid.Row>
188
- {description && (
189
- <Grid.Row stretched>
190
- <Grid.Column stretched width="12">
191
- <p className="help">
192
- {this.props.multilingual_options
193
- ? `${intl.formatMessage(messages.language_independent)} `
194
- : null}
195
- {description}
196
- </p>
197
- </Grid.Column>
198
- </Grid.Row>
199
- )}
200
- </Grid>
201
- </Form.Field>
202
- ) : (
203
- <>{wdg}</>
204
- );
205
- }
206
- }
155
+ )}
156
+ </Grid>
157
+ </Form.Field>
158
+ ) : (
159
+ <>{wdg}</>
160
+ );
161
+ };
162
+
163
+ /**
164
+ * Property types.
165
+ * @property {Object} propTypes Property types.
166
+ */
167
+ FormFieldWrapper.propTypes = {
168
+ id: PropTypes.string.isRequired,
169
+ title: PropTypes.string.isRequired,
170
+ description: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
171
+ required: PropTypes.bool,
172
+ error: PropTypes.arrayOf(PropTypes.string),
173
+ wrapped: PropTypes.bool,
174
+ columns: PropTypes.number,
175
+ draggable: PropTypes.bool,
176
+ isDisabled: PropTypes.bool,
177
+ onEdit: PropTypes.func,
178
+ className: PropTypes.string,
179
+ onDelete: PropTypes.func,
180
+ fieldSet: PropTypes.string,
181
+ noForInFieldLabel: PropTypes.bool,
182
+ multilingual_options: PropTypes.object,
183
+ children: PropTypes.node,
184
+ };
207
185
 
208
- export default injectIntl(FormFieldWrapper);
186
+ export default FormFieldWrapper;
@@ -305,8 +305,13 @@ const apiMiddlewareFactory =
305
305
  (error) => {
306
306
  // Make sure an error during hydration
307
307
  // (for example when serving an archived page)
308
- // doesn't hide the SSR content.
309
- if (isHydrating && !hasExistingError) {
308
+ // doesn't hide the SSR content. Only suppress GET_CONTENT failures;
309
+ // user-initiated actions (LOGIN, etc.) must always dispatch _FAIL so
310
+ // loaders stop and errors surface.
311
+ const shouldIgnoreHydrationError =
312
+ isHydrating && !hasExistingError && type === GET_CONTENT;
313
+
314
+ if (shouldIgnoreHydrationError) {
310
315
  isHydrating = false;
311
316
  return;
312
317
  }
@@ -1,5 +1,28 @@
1
- declare const _default: React.FC<import("react-intl").WithIntlProps<any>> & {
2
- WrappedComponent: React.ComponentType<any>;
3
- };
4
- export default _default;
5
- import React from 'react';
1
+ export default FormFieldWrapper;
2
+ /**
3
+ * FormFieldWrapper component.
4
+ * @function FormFieldWrapper
5
+ * @param {Object} props - Component props
6
+ * @returns {JSX.Element} Markup for the component.
7
+ */
8
+ declare function FormFieldWrapper({ id, title, description, fieldSet, required, error, wrapped, columns, draggable, onEdit, className, isDisabled, onDelete, noForInFieldLabel, multilingual_options, children, }: any): JSX.Element;
9
+ declare namespace FormFieldWrapper {
10
+ namespace propTypes {
11
+ let id: any;
12
+ let title: any;
13
+ let description: any;
14
+ let required: any;
15
+ let error: any;
16
+ let wrapped: any;
17
+ let columns: any;
18
+ let draggable: any;
19
+ let isDisabled: any;
20
+ let onEdit: any;
21
+ let className: any;
22
+ let onDelete: any;
23
+ let fieldSet: any;
24
+ let noForInFieldLabel: any;
25
+ let multilingual_options: any;
26
+ let children: any;
27
+ }
28
+ }
@@ -121,4 +121,4 @@ export declare const ColorPickerWidget: import("@loadable/component").LoadableCo
121
121
  export declare const DatetimeWidget: import("@loadable/component").LoadableClassComponent<any>;
122
122
  export declare const TimeWidget: import("@loadable/component").LoadableClassComponent<any>;
123
123
  export declare const RecurrenceWidget: import("@loadable/component").LoadableClassComponent<any>;
124
- export declare const FormFieldWrapper: import("@loadable/component").LoadableComponent<import("react-intl").WithIntlProps<any>>;
124
+ export declare const FormFieldWrapper: import("@loadable/component").LoadableComponent<any>;