@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/CHANGELOG.md +19 -0
- package/README.md +18 -12
- package/locales/it/LC_MESSAGES/volto.po +7 -6
- package/locales/it.json +1 -1
- package/locales/ta/LC_MESSAGES/volto.po +3 -3
- package/locales/ta.json +1 -1
- package/package.json +3 -3
- package/src/components/manage/Form/ModalForm.jsx +12 -10
- package/src/components/manage/Form/ModalForm.test.jsx +26 -1
- package/src/components/manage/Widgets/DatetimeWidget.jsx +11 -1
- package/src/components/manage/Widgets/FormFieldWrapper.jsx +146 -168
- package/src/middleware/api.js +7 -2
- package/types/components/manage/Widgets/FormFieldWrapper.d.ts +28 -5
- package/types/components/manage/Widgets/index.d.ts +1 -1
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
}
|
|
10
10
|
],
|
|
11
11
|
"license": "MIT",
|
|
12
|
-
"version": "18.
|
|
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 =
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
35
|
-
* @
|
|
36
|
-
* @
|
|
34
|
+
* FormFieldWrapper component.
|
|
35
|
+
* @function FormFieldWrapper
|
|
36
|
+
* @param {Object} props - Component props
|
|
37
|
+
* @returns {JSX.Element} Markup for the component.
|
|
37
38
|
*/
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
60
|
+
const wdg = (
|
|
61
|
+
<>
|
|
62
|
+
{children}
|
|
107
63
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
|
186
|
+
export default FormFieldWrapper;
|
package/src/middleware/api.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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<
|
|
124
|
+
export declare const FormFieldWrapper: import("@loadable/component").LoadableComponent<any>;
|