@plone/volto 19.0.0-alpha.17 → 19.0.0-alpha.19
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 +32 -4
- package/locales/af.json +1 -1
- package/locales/ar.json +1 -1
- package/locales/bg.json +1 -1
- package/locales/bn.json +1 -1
- package/locales/ca/LC_MESSAGES/volto.po +33 -0
- package/locales/ca.json +1 -1
- package/locales/cs.json +1 -1
- package/locales/cy.json +1 -1
- package/locales/da.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +33 -0
- package/locales/de.json +1 -1
- package/locales/el.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +33 -0
- package/locales/en.json +1 -1
- package/locales/en_AU.json +1 -1
- package/locales/en_GB.json +1 -1
- package/locales/eo.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +33 -0
- package/locales/es.json +1 -1
- package/locales/et.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +33 -0
- package/locales/eu.json +1 -1
- package/locales/fa.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +33 -0
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +33 -0
- package/locales/fr.json +1 -1
- package/locales/fu.json +1 -1
- package/locales/gl.json +1 -1
- package/locales/he.json +1 -1
- package/locales/hi/LC_MESSAGES/volto.po +33 -0
- package/locales/hi.json +1 -1
- package/locales/hr.json +1 -1
- package/locales/hu.json +1 -1
- package/locales/hy.json +1 -1
- package/locales/id.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +33 -0
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +33 -0
- package/locales/ja.json +1 -1
- package/locales/ka.json +1 -1
- package/locales/kn.json +1 -1
- package/locales/ko.json +1 -1
- package/locales/lt.json +1 -1
- package/locales/lv.json +1 -1
- package/locales/mi.json +1 -1
- package/locales/mk.json +1 -1
- package/locales/my.json +1 -1
- package/locales/nb_NO.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +33 -0
- package/locales/nl.json +1 -1
- package/locales/nn.json +1 -1
- package/locales/pl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +33 -0
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +33 -0
- package/locales/pt_BR.json +1 -1
- package/locales/rm.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +33 -0
- package/locales/ro.json +1 -1
- package/locales/ru/LC_MESSAGES/volto.po +33 -0
- package/locales/ru.json +1 -1
- package/locales/sk.json +1 -1
- package/locales/sl.json +1 -1
- package/locales/sm.json +1 -1
- package/locales/sq.json +1 -1
- package/locales/sr.json +1 -1
- package/locales/sr@cyrl.json +1 -1
- package/locales/sr@latn.json +1 -1
- package/locales/sv.json +1 -1
- package/locales/ta.json +1 -1
- package/locales/te.json +1 -1
- package/locales/th.json +1 -1
- package/locales/to.json +1 -1
- package/locales/tr.json +1 -1
- package/locales/uk.json +1 -1
- package/locales/vi.json +1 -1
- package/locales/volto.pot +34 -1
- package/locales/zh_CN/LC_MESSAGES/volto.po +33 -0
- package/locales/zh_CN.json +1 -1
- package/locales/zh_Hant.json +1 -1
- package/locales/zh_Hant_HK.json +1 -1
- package/package.json +6 -6
- package/razzle.config.js +3 -19
- package/src/components/manage/BlockChooser/BlockChooser.jsx +1 -0
- package/src/components/manage/Contents/Contents.jsx +685 -671
- package/src/components/manage/Contents/DropZoneContent.jsx +323 -0
- package/src/components/manage/Form/ModalForm.jsx +12 -10
- package/src/components/manage/Form/ModalForm.test.jsx +26 -0
- package/src/components/manage/Sidebar/ObjectBrowser.jsx +3 -0
- package/src/components/manage/Sidebar/ObjectBrowserBody.jsx +13 -1
- package/src/components/manage/Widgets/ObjectBrowserWidget.jsx +5 -0
- package/src/components/manage/Widgets/QueryWidget.jsx +136 -9
- package/src/components/theme/Unauthorized/Unauthorized.jsx +5 -2
- package/src/components/theme/View/View.jsx +7 -0
- package/src/helpers/Url/Url.js +1 -0
- package/src/reducers/querystring/querystring.js +8 -1
- package/theme/themes/pastanaga/extras/contents.less +63 -0
- package/theme/themes/pastanaga/extras/widgets.less +34 -0
- package/types/components/manage/Contents/DropZoneContent.d.ts +2 -0
- package/types/components/manage/Controlpanels/Relations/RelationsMatrix.d.ts +1 -1
- package/types/components/manage/Multilingual/ManageTranslations.d.ts +1 -1
- package/types/components/manage/Sidebar/ObjectBrowser.d.ts +1 -1
- package/types/components/manage/Widgets/ImageWidget.d.ts +1 -1
- package/types/components/manage/Widgets/InternalUrlWidget.d.ts +1 -1
- package/types/components/manage/Widgets/ObjectBrowserWidget.d.ts +2 -0
- package/types/components/manage/Widgets/QueryWidget.d.ts +5 -2
- package/types/components/manage/Widgets/UrlWidget.d.ts +1 -1
- package/types/components/manage/Widgets/index.d.ts +3 -3
- package/types/components/theme/Unauthorized/Unauthorized.d.ts +2 -2
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
|
|
3
|
+
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
Modal,
|
|
7
|
+
Table,
|
|
8
|
+
Input,
|
|
9
|
+
Dimmer,
|
|
10
|
+
Progress,
|
|
11
|
+
} from 'semantic-ui-react';
|
|
12
|
+
import cx from 'classnames';
|
|
13
|
+
import filesize from 'filesize';
|
|
14
|
+
import { readAsDataURL } from 'promise-file-reader';
|
|
15
|
+
|
|
16
|
+
import { createContent } from '@plone/volto/actions/content/content';
|
|
17
|
+
import { usePrevious } from '@plone/volto/helpers/Utils/usePrevious';
|
|
18
|
+
import { validateFileUploadSize } from '@plone/volto/helpers/FormValidation/FormValidation';
|
|
19
|
+
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
20
|
+
import uploadSVG from '@plone/volto/icons/upload.svg';
|
|
21
|
+
import clearSVG from '@plone/volto/icons/clear.svg';
|
|
22
|
+
import FormattedRelativeDate from '@plone/volto/components/theme/FormattedDate/FormattedRelativeDate';
|
|
23
|
+
import Image from '@plone/volto/components/theme/Image/Image';
|
|
24
|
+
|
|
25
|
+
const SUBREQUEST = 'batch-upload';
|
|
26
|
+
|
|
27
|
+
const messages = defineMessages({
|
|
28
|
+
cancel: {
|
|
29
|
+
id: 'Cancel',
|
|
30
|
+
defaultMessage: 'Cancel',
|
|
31
|
+
},
|
|
32
|
+
upload: {
|
|
33
|
+
id: '{count, plural, one {Upload {count} file} other {Upload {count} files}}',
|
|
34
|
+
defaultMessage:
|
|
35
|
+
'{count, plural, one {Upload {count} file} other {Upload {count} files}}',
|
|
36
|
+
},
|
|
37
|
+
filesUploaded: {
|
|
38
|
+
id: 'Files uploaded: {uploadedFiles}',
|
|
39
|
+
defaultMessage: 'Files uploaded: {uploadedFiles}',
|
|
40
|
+
},
|
|
41
|
+
dropFiles: {
|
|
42
|
+
id: 'Drop files here to upload',
|
|
43
|
+
defaultMessage: 'Drop files here to upload',
|
|
44
|
+
},
|
|
45
|
+
releaseToAdd: {
|
|
46
|
+
id: 'Release to add file(s) to this folder',
|
|
47
|
+
defaultMessage: 'Release to add file(s) to this folder',
|
|
48
|
+
},
|
|
49
|
+
totalFilesToUpload: {
|
|
50
|
+
id: 'Total files to upload: {totalFiles}',
|
|
51
|
+
defaultMessage: 'Total files to upload: {totalFiles}',
|
|
52
|
+
},
|
|
53
|
+
uploadFiles: {
|
|
54
|
+
id: 'Upload Files ({count})',
|
|
55
|
+
defaultMessage: 'Upload Files ({count})',
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const DropZoneContent = (props) => {
|
|
60
|
+
const { onOk, onCancel, pathname, children } = props;
|
|
61
|
+
const [isDragOver, setIsDragOver] = useState(false);
|
|
62
|
+
const [showModal, setShowModal] = useState(false);
|
|
63
|
+
const [droppedFiles, setDroppedFiles] = useState([]);
|
|
64
|
+
const [totalFiles, setTotalFiles] = useState(0);
|
|
65
|
+
|
|
66
|
+
const intl = useIntl();
|
|
67
|
+
const dispatch = useDispatch();
|
|
68
|
+
|
|
69
|
+
const request = useSelector(
|
|
70
|
+
(state) => state.content.subrequests?.[SUBREQUEST] || {},
|
|
71
|
+
shallowEqual,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const uploadedFiles = useSelector((state) => state.content.uploadedFiles);
|
|
75
|
+
const prevrequestloading = usePrevious(request.loading);
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (prevrequestloading && request.loaded) {
|
|
79
|
+
onOk();
|
|
80
|
+
setDroppedFiles([]);
|
|
81
|
+
}
|
|
82
|
+
}, [prevrequestloading, request.loaded, onOk]);
|
|
83
|
+
|
|
84
|
+
const handleDragEnter = (e) => {
|
|
85
|
+
e.preventDefault();
|
|
86
|
+
e.stopPropagation();
|
|
87
|
+
setIsDragOver(true);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const handleDragLeave = (e) => {
|
|
91
|
+
e.preventDefault();
|
|
92
|
+
e.stopPropagation();
|
|
93
|
+
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
94
|
+
setIsDragOver(false);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const handleDragOver = (e) => {
|
|
99
|
+
e.preventDefault();
|
|
100
|
+
e.stopPropagation();
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const onDrop = async (e) => {
|
|
104
|
+
setIsDragOver(false);
|
|
105
|
+
const newFiles = Array.from(e.dataTransfer.files);
|
|
106
|
+
const validFiles = [];
|
|
107
|
+
for (let i = 0; i < newFiles.length; i++) {
|
|
108
|
+
if (validateFileUploadSize(newFiles[i], intl.formatMessage)) {
|
|
109
|
+
await readAsDataURL(newFiles[i]).then((data) => {
|
|
110
|
+
const fields = data.match(/^data:(.*);(.*),(.*)$/);
|
|
111
|
+
newFiles[i].preview = fields[0];
|
|
112
|
+
});
|
|
113
|
+
validFiles.push(newFiles[i]);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
setDroppedFiles((prev) => prev.concat(newFiles));
|
|
117
|
+
setTotalFiles((prev) => prev + validFiles.length);
|
|
118
|
+
setShowModal(true);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const handleCloseModal = () => {
|
|
122
|
+
setShowModal(false);
|
|
123
|
+
onCancel();
|
|
124
|
+
setDroppedFiles([]);
|
|
125
|
+
setTotalFiles(0);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const onSubmit = () => {
|
|
129
|
+
Promise.all(droppedFiles.map((file) => readAsDataURL(file))).then(
|
|
130
|
+
(dataUrls) => {
|
|
131
|
+
dispatch(
|
|
132
|
+
createContent(
|
|
133
|
+
pathname,
|
|
134
|
+
droppedFiles.map((file, index) => {
|
|
135
|
+
const fields = dataUrls[index].match(/^data:(.*);(.*),(.*)$/);
|
|
136
|
+
const image = fields[1].split('/')[0] === 'image';
|
|
137
|
+
return {
|
|
138
|
+
'@type': image ? 'Image' : 'File',
|
|
139
|
+
title: file.name,
|
|
140
|
+
[image ? 'image' : 'file']: {
|
|
141
|
+
data: fields[3],
|
|
142
|
+
encoding: fields[2],
|
|
143
|
+
'content-type': fields[1],
|
|
144
|
+
filename: file.name,
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}),
|
|
148
|
+
SUBREQUEST,
|
|
149
|
+
),
|
|
150
|
+
);
|
|
151
|
+
},
|
|
152
|
+
);
|
|
153
|
+
handleCloseModal();
|
|
154
|
+
};
|
|
155
|
+
const onRemoveFile = (index) => {
|
|
156
|
+
const updatedFiles = droppedFiles.filter((file, i) => i !== index);
|
|
157
|
+
setDroppedFiles(updatedFiles);
|
|
158
|
+
setTotalFiles(updatedFiles.length);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const onChangeFileName = (e, index) => {
|
|
162
|
+
let copyOfFiles = [...droppedFiles];
|
|
163
|
+
let originalFile = droppedFiles[index];
|
|
164
|
+
let newFile = new File([originalFile], e.target.value, {
|
|
165
|
+
type: originalFile.type,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
newFile.preview = originalFile.preview;
|
|
169
|
+
newFile.path = e.target.value;
|
|
170
|
+
copyOfFiles[index] = newFile;
|
|
171
|
+
setDroppedFiles(copyOfFiles);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<>
|
|
176
|
+
<div
|
|
177
|
+
className={cx('contents-dropzone', {
|
|
178
|
+
'drag-over': isDragOver,
|
|
179
|
+
'drag-inactive': !isDragOver,
|
|
180
|
+
})}
|
|
181
|
+
onDragEnter={handleDragEnter}
|
|
182
|
+
onDragLeave={handleDragLeave}
|
|
183
|
+
onDragOver={handleDragOver}
|
|
184
|
+
onDrop={onDrop}
|
|
185
|
+
>
|
|
186
|
+
{children}
|
|
187
|
+
{isDragOver && (
|
|
188
|
+
<div className="dropzone-overlay">
|
|
189
|
+
<div className="dropzone-content">
|
|
190
|
+
<Icon name={uploadSVG} size="48px" />
|
|
191
|
+
<h3>{intl.formatMessage(messages.dropFiles)}</h3>
|
|
192
|
+
<p>{intl.formatMessage(messages.releaseToAdd)}</p>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
)}
|
|
196
|
+
</div>
|
|
197
|
+
<Modal
|
|
198
|
+
open={showModal}
|
|
199
|
+
onClose={handleCloseModal}
|
|
200
|
+
className="contents-upload-modal"
|
|
201
|
+
>
|
|
202
|
+
<Modal.Header>
|
|
203
|
+
{intl.formatMessage(messages.uploadFiles, {
|
|
204
|
+
count: droppedFiles.length,
|
|
205
|
+
})}
|
|
206
|
+
</Modal.Header>
|
|
207
|
+
<Dimmer active={request.loading}>
|
|
208
|
+
<div className="progress-container">
|
|
209
|
+
<Progress
|
|
210
|
+
className="progress-bar"
|
|
211
|
+
value={uploadedFiles}
|
|
212
|
+
total={totalFiles}
|
|
213
|
+
>
|
|
214
|
+
{intl.formatMessage(messages.filesUploaded, {
|
|
215
|
+
uploadedFiles,
|
|
216
|
+
})}
|
|
217
|
+
<br />
|
|
218
|
+
{intl.formatMessage(messages.totalFilesToUpload, {
|
|
219
|
+
totalFiles,
|
|
220
|
+
})}
|
|
221
|
+
</Progress>
|
|
222
|
+
</div>
|
|
223
|
+
</Dimmer>
|
|
224
|
+
<Modal.Content>
|
|
225
|
+
{droppedFiles.length > 0 && (
|
|
226
|
+
<Table compact singleLine>
|
|
227
|
+
<Table.Header>
|
|
228
|
+
<Table.Row>
|
|
229
|
+
<Table.HeaderCell width={8}>
|
|
230
|
+
<FormattedMessage id="Filename" defaultMessage="Filename" />
|
|
231
|
+
</Table.HeaderCell>
|
|
232
|
+
<Table.HeaderCell width={4}>
|
|
233
|
+
<FormattedMessage
|
|
234
|
+
id="Last modified"
|
|
235
|
+
defaultMessage="Last modified"
|
|
236
|
+
/>
|
|
237
|
+
</Table.HeaderCell>
|
|
238
|
+
<Table.HeaderCell width={4}>
|
|
239
|
+
<FormattedMessage
|
|
240
|
+
id="File size"
|
|
241
|
+
defaultMessage="File size"
|
|
242
|
+
/>
|
|
243
|
+
</Table.HeaderCell>
|
|
244
|
+
<Table.HeaderCell width={4}>
|
|
245
|
+
<FormattedMessage id="Preview" defaultMessage="Preview" />
|
|
246
|
+
</Table.HeaderCell>
|
|
247
|
+
<Table.HeaderCell />
|
|
248
|
+
</Table.Row>
|
|
249
|
+
</Table.Header>
|
|
250
|
+
<Table.Body>
|
|
251
|
+
{droppedFiles.map((file, index) => (
|
|
252
|
+
<Table.Row className="upload-row" key={index}>
|
|
253
|
+
<Table.Cell>
|
|
254
|
+
<Input
|
|
255
|
+
className="file-name"
|
|
256
|
+
value={file.name}
|
|
257
|
+
onChange={(e) => onChangeFileName(e, index)}
|
|
258
|
+
/>
|
|
259
|
+
</Table.Cell>
|
|
260
|
+
<Table.Cell>
|
|
261
|
+
{file.lastModifiedDate && (
|
|
262
|
+
<FormattedRelativeDate date={file.lastModifiedDate} />
|
|
263
|
+
)}
|
|
264
|
+
</Table.Cell>
|
|
265
|
+
<Table.Cell>{filesize(file.size, { round: 0 })}</Table.Cell>
|
|
266
|
+
<Table.Cell>
|
|
267
|
+
{file.type.split('/')[0] === 'image' && (
|
|
268
|
+
<Image
|
|
269
|
+
src={file.preview}
|
|
270
|
+
height={60}
|
|
271
|
+
className="ui image"
|
|
272
|
+
/>
|
|
273
|
+
)}
|
|
274
|
+
</Table.Cell>
|
|
275
|
+
<Table.Cell>
|
|
276
|
+
<Icon
|
|
277
|
+
name={clearSVG}
|
|
278
|
+
size="24px"
|
|
279
|
+
onClick={() => onRemoveFile(index)}
|
|
280
|
+
/>
|
|
281
|
+
</Table.Cell>
|
|
282
|
+
</Table.Row>
|
|
283
|
+
))}
|
|
284
|
+
</Table.Body>
|
|
285
|
+
</Table>
|
|
286
|
+
)}
|
|
287
|
+
</Modal.Content>
|
|
288
|
+
<Modal.Actions>
|
|
289
|
+
{droppedFiles.length > 0 && (
|
|
290
|
+
<Button
|
|
291
|
+
basic
|
|
292
|
+
circular
|
|
293
|
+
primary
|
|
294
|
+
floated="right"
|
|
295
|
+
icon="arrow right"
|
|
296
|
+
aria-label={intl.formatMessage(messages.upload, {
|
|
297
|
+
count: droppedFiles.length,
|
|
298
|
+
})}
|
|
299
|
+
onClick={onSubmit}
|
|
300
|
+
title={intl.formatMessage(messages.upload, {
|
|
301
|
+
count: droppedFiles.length,
|
|
302
|
+
})}
|
|
303
|
+
size="big"
|
|
304
|
+
/>
|
|
305
|
+
)}
|
|
306
|
+
<Button
|
|
307
|
+
basic
|
|
308
|
+
circular
|
|
309
|
+
secondary
|
|
310
|
+
icon="remove"
|
|
311
|
+
aria-label={intl.formatMessage(messages.cancel)}
|
|
312
|
+
title={intl.formatMessage(messages.cancel)}
|
|
313
|
+
floated="right"
|
|
314
|
+
size="big"
|
|
315
|
+
onClick={handleCloseModal}
|
|
316
|
+
/>
|
|
317
|
+
</Modal.Actions>
|
|
318
|
+
</Modal>
|
|
319
|
+
</>
|
|
320
|
+
);
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
export default DropZoneContent;
|
|
@@ -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
|
|
@@ -89,4 +89,30 @@ describe('ModalForm', () => {
|
|
|
89
89
|
const loadingMessage = getByText(/renaming items.../i);
|
|
90
90
|
expect(loadingMessage).toBeInTheDocument();
|
|
91
91
|
});
|
|
92
|
+
|
|
93
|
+
it('renders with empty fieldsets array', () => {
|
|
94
|
+
const store = mockStore({
|
|
95
|
+
intl: {
|
|
96
|
+
locale: 'en',
|
|
97
|
+
messages: {},
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
const component = renderer.create(
|
|
101
|
+
<Provider store={store}>
|
|
102
|
+
<ModalForm
|
|
103
|
+
schema={{
|
|
104
|
+
fieldsets: [],
|
|
105
|
+
properties: {},
|
|
106
|
+
required: [],
|
|
107
|
+
}}
|
|
108
|
+
onSubmit={() => {}}
|
|
109
|
+
onCancel={() => {}}
|
|
110
|
+
open={false}
|
|
111
|
+
title="Action without form"
|
|
112
|
+
/>
|
|
113
|
+
</Provider>,
|
|
114
|
+
);
|
|
115
|
+
const json = component.toJSON();
|
|
116
|
+
expect(json).toMatchSnapshot();
|
|
117
|
+
});
|
|
92
118
|
});
|
|
@@ -58,6 +58,7 @@ const withObjectBrowser = (WrappedComponent) =>
|
|
|
58
58
|
selectableTypes,
|
|
59
59
|
maximumSelectionSize,
|
|
60
60
|
currentPath,
|
|
61
|
+
onlyFolderishSelectable,
|
|
61
62
|
} = {}) =>
|
|
62
63
|
this.setState(() => ({
|
|
63
64
|
isObjectBrowserOpen: true,
|
|
@@ -70,6 +71,7 @@ const withObjectBrowser = (WrappedComponent) =>
|
|
|
70
71
|
selectableTypes,
|
|
71
72
|
maximumSelectionSize,
|
|
72
73
|
currentPath,
|
|
74
|
+
onlyFolderishSelectable,
|
|
73
75
|
}));
|
|
74
76
|
|
|
75
77
|
closeObjectBrowser = () => this.setState({ isObjectBrowserOpen: false });
|
|
@@ -110,6 +112,7 @@ const withObjectBrowser = (WrappedComponent) =>
|
|
|
110
112
|
searchableTypes={this.state.searchableTypes}
|
|
111
113
|
selectableTypes={this.state.selectableTypes}
|
|
112
114
|
maximumSelectionSize={this.state.maximumSelectionSize}
|
|
115
|
+
onlyFolderishSelectable={this.state.onlyFolderishSelectable}
|
|
113
116
|
/>
|
|
114
117
|
</SidebarPopup>
|
|
115
118
|
</>
|
|
@@ -86,6 +86,7 @@ class ObjectBrowserBody extends Component {
|
|
|
86
86
|
maximumSelectionSize: PropTypes.number,
|
|
87
87
|
contextURL: PropTypes.string,
|
|
88
88
|
searchableTypes: PropTypes.arrayOf(PropTypes.string),
|
|
89
|
+
onlyFolderishSelectable: PropTypes.bool,
|
|
89
90
|
};
|
|
90
91
|
|
|
91
92
|
/**
|
|
@@ -101,6 +102,7 @@ class ObjectBrowserBody extends Component {
|
|
|
101
102
|
selectableTypes: [],
|
|
102
103
|
searchableTypes: null,
|
|
103
104
|
maximumSelectionSize: null,
|
|
105
|
+
onlyFolderishSelectable: false,
|
|
104
106
|
};
|
|
105
107
|
|
|
106
108
|
/**
|
|
@@ -329,7 +331,17 @@ class ObjectBrowserBody extends Component {
|
|
|
329
331
|
};
|
|
330
332
|
|
|
331
333
|
isSelectable = (item) => {
|
|
332
|
-
const {
|
|
334
|
+
const {
|
|
335
|
+
maximumSelectionSize,
|
|
336
|
+
data,
|
|
337
|
+
mode,
|
|
338
|
+
selectableTypes,
|
|
339
|
+
onlyFolderishSelectable,
|
|
340
|
+
} = this.props;
|
|
341
|
+
|
|
342
|
+
if (onlyFolderishSelectable && !item.is_folderish) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
333
345
|
if (
|
|
334
346
|
maximumSelectionSize &&
|
|
335
347
|
data &&
|
|
@@ -82,6 +82,7 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
82
82
|
openObjectBrowser: PropTypes.func.isRequired,
|
|
83
83
|
allowExternals: PropTypes.bool,
|
|
84
84
|
placeholder: PropTypes.string,
|
|
85
|
+
onlyFolderishSelectable: PropTypes.bool,
|
|
85
86
|
};
|
|
86
87
|
|
|
87
88
|
/**
|
|
@@ -98,6 +99,7 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
98
99
|
return: 'multiple',
|
|
99
100
|
initialPath: '',
|
|
100
101
|
allowExternals: false,
|
|
102
|
+
onlyFolderishSelectable: false,
|
|
101
103
|
};
|
|
102
104
|
|
|
103
105
|
state = {
|
|
@@ -315,6 +317,9 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
315
317
|
maximumSelectionSize:
|
|
316
318
|
this.props.widgetOptions?.pattern_options?.maximumSelectionSize ||
|
|
317
319
|
this.props.maximumSelectionSize,
|
|
320
|
+
onlyFolderishSelectable:
|
|
321
|
+
this.props.widgetOptions?.pattern_options?.onlyFolderishSelectable ||
|
|
322
|
+
this.props.onlyFolderishSelectable,
|
|
318
323
|
});
|
|
319
324
|
};
|
|
320
325
|
|