@plone/volto 18.34.0 → 18.35.1
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/.release-it.json +3 -0
- package/CHANGELOG.md +60 -0
- package/locales/af/LC_MESSAGES/volto.po +35 -5
- package/locales/af.json +1 -1
- package/locales/ar/LC_MESSAGES/volto.po +35 -5
- package/locales/ar.json +1 -1
- package/locales/bg/LC_MESSAGES/volto.po +35 -5
- package/locales/bg.json +1 -1
- package/locales/bn/LC_MESSAGES/volto.po +35 -5
- package/locales/bn.json +1 -1
- package/locales/ca/LC_MESSAGES/volto.po +37 -7
- package/locales/ca.json +1 -1
- package/locales/cs/LC_MESSAGES/volto.po +35 -5
- package/locales/cs.json +1 -1
- package/locales/cy/LC_MESSAGES/volto.po +35 -5
- package/locales/cy.json +1 -1
- package/locales/da/LC_MESSAGES/volto.po +35 -5
- package/locales/da.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +39 -9
- package/locales/de.json +1 -1
- package/locales/el/LC_MESSAGES/volto.po +35 -5
- package/locales/el.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +35 -5
- package/locales/en.json +1 -1
- package/locales/en_AU/LC_MESSAGES/volto.po +35 -5
- package/locales/en_AU.json +1 -1
- package/locales/en_GB/LC_MESSAGES/volto.po +35 -5
- package/locales/en_GB.json +1 -1
- package/locales/eo/LC_MESSAGES/volto.po +35 -5
- package/locales/eo.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +36 -6
- package/locales/es.json +1 -1
- package/locales/et/LC_MESSAGES/volto.po +35 -5
- package/locales/et.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +38 -8
- package/locales/eu.json +1 -1
- package/locales/fa/LC_MESSAGES/volto.po +35 -5
- package/locales/fa.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +35 -5
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +36 -6
- package/locales/fr.json +1 -1
- package/locales/fu/LC_MESSAGES/volto.po +35 -5
- package/locales/fu.json +1 -1
- package/locales/gl/LC_MESSAGES/volto.po +35 -5
- package/locales/gl.json +1 -1
- package/locales/he/LC_MESSAGES/volto.po +35 -5
- package/locales/he.json +1 -1
- package/locales/hi/LC_MESSAGES/volto.po +38 -8
- package/locales/hi.json +1 -1
- package/locales/hr/LC_MESSAGES/volto.po +35 -5
- package/locales/hr.json +1 -1
- package/locales/hu/LC_MESSAGES/volto.po +35 -5
- package/locales/hu.json +1 -1
- package/locales/hy/LC_MESSAGES/volto.po +35 -5
- package/locales/hy.json +1 -1
- package/locales/id/LC_MESSAGES/volto.po +35 -5
- package/locales/id.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +36 -6
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +35 -5
- package/locales/ja.json +1 -1
- package/locales/ka/LC_MESSAGES/volto.po +35 -5
- package/locales/ka.json +1 -1
- package/locales/kn/LC_MESSAGES/volto.po +35 -5
- package/locales/kn.json +1 -1
- package/locales/ko/LC_MESSAGES/volto.po +35 -5
- package/locales/ko.json +1 -1
- package/locales/lt/LC_MESSAGES/volto.po +35 -5
- package/locales/lt.json +1 -1
- package/locales/lv/LC_MESSAGES/volto.po +35 -5
- package/locales/lv.json +1 -1
- package/locales/mi/LC_MESSAGES/volto.po +35 -5
- package/locales/mi.json +1 -1
- package/locales/mk/LC_MESSAGES/volto.po +35 -5
- package/locales/mk.json +1 -1
- package/locales/my/LC_MESSAGES/volto.po +35 -5
- package/locales/my.json +1 -1
- package/locales/nb_NO/LC_MESSAGES/volto.po +35 -5
- package/locales/nb_NO.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +36 -6
- package/locales/nl.json +1 -1
- package/locales/nn/LC_MESSAGES/volto.po +35 -5
- package/locales/nn.json +1 -1
- package/locales/pl/LC_MESSAGES/volto.po +35 -5
- package/locales/pl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +35 -5
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +49 -19
- package/locales/pt_BR.json +1 -1
- package/locales/rm/LC_MESSAGES/volto.po +35 -5
- package/locales/rm.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +36 -6
- package/locales/ro.json +1 -1
- package/locales/ru/LC_MESSAGES/volto.po +36 -6
- package/locales/ru.json +1 -1
- package/locales/sk/LC_MESSAGES/volto.po +35 -5
- package/locales/sk.json +1 -1
- package/locales/sl/LC_MESSAGES/volto.po +35 -5
- package/locales/sl.json +1 -1
- package/locales/sm/LC_MESSAGES/volto.po +35 -5
- package/locales/sm.json +1 -1
- package/locales/sq/LC_MESSAGES/volto.po +35 -5
- package/locales/sq.json +1 -1
- package/locales/sr/LC_MESSAGES/volto.po +35 -5
- package/locales/sr.json +1 -1
- package/locales/sr@cyrl/LC_MESSAGES/volto.po +35 -5
- package/locales/sr@cyrl.json +1 -1
- package/locales/sr@latn/LC_MESSAGES/volto.po +35 -5
- package/locales/sr@latn.json +1 -1
- package/locales/sv/LC_MESSAGES/volto.po +35 -5
- package/locales/sv.json +1 -1
- package/locales/ta/LC_MESSAGES/volto.po +36 -6
- package/locales/ta.json +1 -1
- package/locales/te/LC_MESSAGES/volto.po +35 -5
- package/locales/te.json +1 -1
- package/locales/th/LC_MESSAGES/volto.po +35 -5
- package/locales/th.json +1 -1
- package/locales/to/LC_MESSAGES/volto.po +35 -5
- package/locales/to.json +1 -1
- package/locales/tr/LC_MESSAGES/volto.po +35 -5
- package/locales/tr.json +1 -1
- package/locales/uk/LC_MESSAGES/volto.po +35 -5
- package/locales/uk.json +1 -1
- package/locales/vi/LC_MESSAGES/volto.po +35 -5
- package/locales/vi.json +1 -1
- package/locales/volto.pot +36 -6
- package/locales/zh_CN/LC_MESSAGES/volto.po +35 -5
- package/locales/zh_CN.json +1 -1
- package/locales/zh_Hant/LC_MESSAGES/volto.po +35 -5
- package/locales/zh_Hant.json +1 -1
- package/locales/zh_Hant_HK/LC_MESSAGES/volto.po +35 -5
- package/locales/zh_Hant_HK.json +1 -1
- package/news/7308.fix +1 -0
- package/news/8084.fix +1 -0
- package/package.json +28 -32
- package/src/actions/querystringsearch/querystringsearch.js +4 -1
- package/src/actions/querystringsearch/querystringsearch.test.js +77 -0
- package/src/components/manage/BlockChooser/BlockChooser.jsx +7 -10
- package/src/components/manage/Blocks/Block/Edit.jsx +9 -10
- package/src/components/manage/Blocks/Block/Order/Item.jsx +9 -4
- package/src/components/manage/Blocks/Image/View.test.jsx +8 -8
- package/src/components/manage/Contents/ContentsBreadcrumbs.jsx +7 -6
- package/src/components/manage/Controlpanels/ContentTypes.jsx +9 -2
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +58 -5
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.ssr.test.jsx +624 -0
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.test.jsx +8 -0
- package/src/components/manage/Form/Form.jsx +6 -1
- package/src/components/manage/Form/ModalForm.jsx +165 -87
- package/src/components/manage/Sidebar/ObjectBrowser.jsx +7 -0
- package/src/components/manage/Sidebar/ObjectBrowserBody.jsx +7 -3
- package/src/components/manage/Sidebar/ObjectBrowserBody.test.jsx +52 -0
- package/src/components/manage/Sidebar/Sidebar.jsx +1 -0
- package/src/components/manage/Toast/Toast.jsx +35 -1
- package/src/components/manage/Toast/Toast.test.jsx +8 -5
- package/src/components/manage/Widgets/ArrayWidget.jsx +3 -2
- package/src/components/manage/Widgets/FormFieldWrapper.jsx +16 -3
- package/src/components/manage/Widgets/ObjectBrowserWidget.jsx +1 -0
- package/src/components/manage/Widgets/QuerystringWidget.jsx +1 -18
- package/src/components/manage/Widgets/QuerystringWidget.test.jsx +45 -2
- package/src/components/manage/Widgets/SelectStyling.jsx +33 -1
- package/src/components/manage/Widgets/SelectWidget.jsx +3 -2
- package/src/components/manage/Widgets/TextWidget.test.jsx +44 -0
- package/src/components/theme/Search/Search.jsx +24 -1
- package/src/components/theme/Unauthorized/Unauthorized.jsx +1 -2
- package/src/components/theme/View/EventView.stories.jsx +89 -0
- package/src/components/theme/View/FileView.stories.jsx +50 -0
- package/src/components/theme/View/LinkView.stories.jsx +57 -0
- package/src/components/theme/View/ListingView.stories.jsx +70 -0
- package/src/components/theme/View/NewsItemView.stories.jsx +58 -0
- package/src/components/theme/View/RenderBlocks.stories.jsx +112 -0
- package/src/components/theme/View/SummaryView.stories.jsx +71 -0
- package/src/components/theme/View/TabularView.stories.jsx +66 -0
- package/src/helpers/FormValidation/validators.ts +15 -2
- package/src/helpers/I18n/I18n.test.ts +44 -0
- package/src/helpers/I18n/I18n.ts +31 -0
- package/src/helpers/index.js +1 -0
- package/src/server.jsx +7 -1
- package/theme/themes/pastanaga/collections/form.overrides +21 -0
- package/theme/themes/pastanaga/elements/button.overrides +30 -3
- package/theme/themes/pastanaga/extras/main.less +1 -0
- package/types/components/manage/Controlpanels/Relations/RelationsMatrix.d.ts +1 -1
- package/types/components/manage/Controlpanels/Users/UsersControlpanel.d.ts +2 -6
- package/types/components/manage/Controlpanels/Users/UsersControlpanel.ssr.test.d.ts +1 -0
- package/types/components/manage/Controlpanels/index.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/Sidebar/ObjectBrowserBody.test.d.ts +1 -0
- 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/QuerystringWidget.d.ts +0 -4
- package/types/components/manage/Widgets/UrlWidget.d.ts +1 -1
- package/types/components/manage/Widgets/index.d.ts +2 -2
- package/types/components/theme/View/EventView.stories.d.ts +19 -0
- package/types/components/theme/View/FileView.stories.d.ts +18 -0
- package/types/components/theme/View/LinkView.stories.d.ts +18 -0
- package/types/components/theme/View/ListingView.stories.d.ts +24 -0
- package/types/components/theme/View/NewsItemView.stories.d.ts +23 -0
- package/types/components/theme/View/RenderBlocks.stories.d.ts +23 -0
- package/types/components/theme/View/SummaryView.stories.d.ts +23 -0
- package/types/components/theme/View/TabularView.stories.d.ts +23 -0
- package/types/helpers/I18n/I18n.d.ts +20 -0
- package/types/helpers/index.d.ts +1 -0
|
@@ -46,6 +46,14 @@ const messages = defineMessages({
|
|
|
46
46
|
id: 'Cancel',
|
|
47
47
|
defaultMessage: 'Cancel',
|
|
48
48
|
},
|
|
49
|
+
dialogOpened: {
|
|
50
|
+
id: 'Pop-up opened: {title}',
|
|
51
|
+
defaultMessage: 'Pop-up opened: {title}',
|
|
52
|
+
},
|
|
53
|
+
dialogClosed: {
|
|
54
|
+
id: 'Pop-up closed.',
|
|
55
|
+
defaultMessage: 'Pop-up closed.',
|
|
56
|
+
},
|
|
49
57
|
});
|
|
50
58
|
|
|
51
59
|
/**
|
|
@@ -54,6 +62,7 @@ const messages = defineMessages({
|
|
|
54
62
|
* @extends Component
|
|
55
63
|
*/
|
|
56
64
|
class ModalForm extends Component {
|
|
65
|
+
static idCounter = 0;
|
|
57
66
|
/**
|
|
58
67
|
* Property types.
|
|
59
68
|
* @property {Object} propTypes Property types.
|
|
@@ -110,17 +119,21 @@ class ModalForm extends Component {
|
|
|
110
119
|
*/
|
|
111
120
|
constructor(props) {
|
|
112
121
|
super(props);
|
|
122
|
+
this.headerId = `modal-title-${++ModalForm.idCounter}`;
|
|
113
123
|
this.state = {
|
|
114
124
|
currentTab: 0,
|
|
115
125
|
errors: {},
|
|
116
126
|
isFormPristine: true,
|
|
117
127
|
formData: props.formData,
|
|
118
128
|
};
|
|
129
|
+
this.modalRef = React.createRef();
|
|
130
|
+
this.announceRef = React.createRef();
|
|
119
131
|
this.selectTab = this.selectTab.bind(this);
|
|
120
132
|
this.onChangeField = this.onChangeField.bind(this);
|
|
121
133
|
this.onBlurField = this.onBlurField.bind(this);
|
|
122
134
|
this.onClickInput = this.onClickInput.bind(this);
|
|
123
135
|
this.onSubmit = this.onSubmit.bind(this);
|
|
136
|
+
this.onKeyDown = this.onKeyDown.bind(this);
|
|
124
137
|
}
|
|
125
138
|
|
|
126
139
|
/**
|
|
@@ -209,12 +222,57 @@ class ModalForm extends Component {
|
|
|
209
222
|
});
|
|
210
223
|
}
|
|
211
224
|
|
|
225
|
+
onKeyDown(event) {
|
|
226
|
+
if (event.key !== 'Tab') return;
|
|
227
|
+
const modal = document.getElementById(this.headerId)?.closest('.ui.modal');
|
|
228
|
+
if (!modal) return;
|
|
229
|
+
const focusable = modal.querySelectorAll(
|
|
230
|
+
'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])',
|
|
231
|
+
);
|
|
232
|
+
if (!focusable.length) return;
|
|
233
|
+
const first = focusable[0];
|
|
234
|
+
const last = focusable[focusable.length - 1];
|
|
235
|
+
if (event.shiftKey) {
|
|
236
|
+
if (document.activeElement === first) {
|
|
237
|
+
event.preventDefault();
|
|
238
|
+
last.focus();
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
if (document.activeElement === last) {
|
|
242
|
+
event.preventDefault();
|
|
243
|
+
first.focus();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
212
248
|
/**
|
|
213
249
|
* Component did update lifecycle handler
|
|
214
250
|
* @param {Object} prevProps
|
|
215
251
|
* @param {Object} prevState
|
|
216
252
|
*/
|
|
217
|
-
|
|
253
|
+
componentWillUnmount() {
|
|
254
|
+
document.removeEventListener('keydown', this.onKeyDown);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
componentDidUpdate(prevProps, prevState) {
|
|
258
|
+
if (!prevProps.open && this.props.open) {
|
|
259
|
+
document.addEventListener('keydown', this.onKeyDown);
|
|
260
|
+
this.modalRef.current?.focus();
|
|
261
|
+
if (this.announceRef.current) {
|
|
262
|
+
this.announceRef.current.textContent = this.props.intl.formatMessage(
|
|
263
|
+
messages.dialogOpened,
|
|
264
|
+
{ title: this.props.title },
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (prevProps.open && !this.props.open) {
|
|
269
|
+
document.removeEventListener('keydown', this.onKeyDown);
|
|
270
|
+
if (this.announceRef.current) {
|
|
271
|
+
this.announceRef.current.textContent = this.props.intl.formatMessage(
|
|
272
|
+
messages.dialogClosed,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
218
276
|
if (this.props.onChangeFormData) {
|
|
219
277
|
if (!isEqual(prevState?.formData, this.state.formData)) {
|
|
220
278
|
this.props.onChangeFormData(this.state.formData);
|
|
@@ -259,100 +317,120 @@ class ModalForm extends Component {
|
|
|
259
317
|
|
|
260
318
|
const state_errors = keys(this.state.errors).length > 0;
|
|
261
319
|
return (
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
''
|
|
320
|
+
<>
|
|
321
|
+
{/* aria-live region outside Modal so it persists through open/close cycles */}
|
|
322
|
+
<div
|
|
323
|
+
ref={this.announceRef}
|
|
324
|
+
aria-live="assertive"
|
|
325
|
+
aria-atomic="true"
|
|
326
|
+
style={{
|
|
327
|
+
position: 'absolute',
|
|
328
|
+
width: '1px',
|
|
329
|
+
height: '1px',
|
|
330
|
+
overflow: 'hidden',
|
|
331
|
+
opacity: 0,
|
|
332
|
+
}}
|
|
333
|
+
/>
|
|
334
|
+
<Modal
|
|
335
|
+
role="dialog"
|
|
336
|
+
dimmer={this.props.dimmer}
|
|
337
|
+
open={this.props.open}
|
|
338
|
+
className={this.props.className}
|
|
339
|
+
aria-labelledby={this.headerId}
|
|
340
|
+
aria-modal="true"
|
|
341
|
+
>
|
|
342
|
+
<Header id={this.headerId}>{this.props.title}</Header>
|
|
343
|
+
<Dimmer active={this.props.loading}>
|
|
344
|
+
<Loader>
|
|
345
|
+
{this.props.loadingMessage || (
|
|
346
|
+
<FormattedMessage id="Loading" defaultMessage="Loading." />
|
|
290
347
|
)}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
348
|
+
</Loader>
|
|
349
|
+
</Dimmer>
|
|
350
|
+
<Modal.Content scrolling>
|
|
351
|
+
{/* outline suppressed for programmatic focus via CSS :focus:not(:focus-visible) on .modal-focus-trap */}
|
|
352
|
+
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
|
|
353
|
+
<div ref={this.modalRef} tabIndex={-1} className="modal-focus-trap">
|
|
354
|
+
<UiForm
|
|
355
|
+
method="post"
|
|
356
|
+
onSubmit={this.onSubmit}
|
|
357
|
+
error={state_errors || Boolean(this.props.submitError)}
|
|
358
|
+
>
|
|
359
|
+
{description}
|
|
360
|
+
<Message error>
|
|
361
|
+
{state_errors ? (
|
|
362
|
+
<FormattedMessage
|
|
363
|
+
id="There were some errors."
|
|
364
|
+
defaultMessage="There were some errors."
|
|
365
|
+
/>
|
|
366
|
+
) : (
|
|
367
|
+
''
|
|
368
|
+
)}
|
|
369
|
+
<div>{this.props.submitError}</div>
|
|
370
|
+
</Message>
|
|
371
|
+
{schema.fieldsets?.length > 1 && (
|
|
372
|
+
<Menu tabular stackable>
|
|
373
|
+
{map(schema.fieldsets, (item, index) => (
|
|
374
|
+
<Menu.Item
|
|
375
|
+
name={item.id}
|
|
376
|
+
index={index}
|
|
377
|
+
key={item.id}
|
|
378
|
+
active={this.state.currentTab === index}
|
|
379
|
+
onClick={this.selectTab}
|
|
380
|
+
>
|
|
381
|
+
{item.title}
|
|
382
|
+
</Menu.Item>
|
|
383
|
+
))}
|
|
384
|
+
</Menu>
|
|
385
|
+
)}
|
|
386
|
+
{fields.map((field) => (
|
|
387
|
+
<Field
|
|
388
|
+
{...field}
|
|
389
|
+
key={field.id}
|
|
390
|
+
onBlur={this.onBlurField}
|
|
391
|
+
onClick={this.onClickInput}
|
|
392
|
+
error={this.state.errors[field.id]}
|
|
393
|
+
/>
|
|
305
394
|
))}
|
|
306
|
-
</
|
|
395
|
+
</UiForm>
|
|
396
|
+
</div>
|
|
397
|
+
</Modal.Content>
|
|
398
|
+
<Modal.Actions>
|
|
399
|
+
{onCancel && (
|
|
400
|
+
<Button
|
|
401
|
+
type="button"
|
|
402
|
+
basic
|
|
403
|
+
circular
|
|
404
|
+
secondary
|
|
405
|
+
aria-label={this.props.intl.formatMessage(messages.cancel)}
|
|
406
|
+
title={this.props.intl.formatMessage(messages.cancel)}
|
|
407
|
+
onClick={onCancel}
|
|
408
|
+
>
|
|
409
|
+
<Icon name={clearSVG} className="circled" size="30px" />
|
|
410
|
+
</Button>
|
|
307
411
|
)}
|
|
308
|
-
{fields.map((field) => (
|
|
309
|
-
<Field
|
|
310
|
-
{...field}
|
|
311
|
-
key={field.id}
|
|
312
|
-
onBlur={this.onBlurField}
|
|
313
|
-
onClick={this.onClickInput}
|
|
314
|
-
error={this.state.errors[field.id]}
|
|
315
|
-
/>
|
|
316
|
-
))}
|
|
317
|
-
</UiForm>
|
|
318
|
-
</Modal.Content>
|
|
319
|
-
<Modal.Actions>
|
|
320
|
-
<Button
|
|
321
|
-
basic
|
|
322
|
-
circular
|
|
323
|
-
primary
|
|
324
|
-
floated="right"
|
|
325
|
-
aria-label={
|
|
326
|
-
this.props.submitLabel
|
|
327
|
-
? this.props.submitLabel
|
|
328
|
-
: this.props.intl.formatMessage(messages.save)
|
|
329
|
-
}
|
|
330
|
-
title={
|
|
331
|
-
this.props.submitLabel
|
|
332
|
-
? this.props.submitLabel
|
|
333
|
-
: this.props.intl.formatMessage(messages.save)
|
|
334
|
-
}
|
|
335
|
-
onClick={this.onSubmit}
|
|
336
|
-
loading={this.props.loading}
|
|
337
|
-
>
|
|
338
|
-
<Icon name={aheadSVG} className="contents circled" size="30px" />
|
|
339
|
-
</Button>
|
|
340
|
-
{onCancel && (
|
|
341
412
|
<Button
|
|
342
|
-
type="button"
|
|
343
413
|
basic
|
|
344
414
|
circular
|
|
345
|
-
|
|
346
|
-
aria-label={
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
415
|
+
primary
|
|
416
|
+
aria-label={
|
|
417
|
+
this.props.submitLabel
|
|
418
|
+
? this.props.submitLabel
|
|
419
|
+
: this.props.intl.formatMessage(messages.save)
|
|
420
|
+
}
|
|
421
|
+
title={
|
|
422
|
+
this.props.submitLabel
|
|
423
|
+
? this.props.submitLabel
|
|
424
|
+
: this.props.intl.formatMessage(messages.save)
|
|
425
|
+
}
|
|
426
|
+
onClick={this.onSubmit}
|
|
427
|
+
loading={this.props.loading}
|
|
350
428
|
>
|
|
351
|
-
<Icon name={
|
|
429
|
+
<Icon name={aheadSVG} className="contents circled" size="30px" />
|
|
352
430
|
</Button>
|
|
353
|
-
|
|
354
|
-
</Modal
|
|
355
|
-
|
|
431
|
+
</Modal.Actions>
|
|
432
|
+
</Modal>
|
|
433
|
+
</>
|
|
356
434
|
);
|
|
357
435
|
}
|
|
358
436
|
}
|
|
@@ -58,6 +58,7 @@ const withObjectBrowser = (WrappedComponent) =>
|
|
|
58
58
|
selectableTypes,
|
|
59
59
|
maximumSelectionSize,
|
|
60
60
|
currentPath,
|
|
61
|
+
initialPath,
|
|
61
62
|
onlyFolderishSelectable,
|
|
62
63
|
} = {}) =>
|
|
63
64
|
this.setState(() => ({
|
|
@@ -71,6 +72,7 @@ const withObjectBrowser = (WrappedComponent) =>
|
|
|
71
72
|
selectableTypes,
|
|
72
73
|
maximumSelectionSize,
|
|
73
74
|
currentPath,
|
|
75
|
+
initialPath,
|
|
74
76
|
onlyFolderishSelectable,
|
|
75
77
|
}));
|
|
76
78
|
|
|
@@ -82,6 +84,10 @@ const withObjectBrowser = (WrappedComponent) =>
|
|
|
82
84
|
this.props.pathname ||
|
|
83
85
|
this.props.location?.pathname;
|
|
84
86
|
|
|
87
|
+
let initialPath = this.state?.initialPath
|
|
88
|
+
? getBaseUrl(this.state.initialPath)
|
|
89
|
+
: null;
|
|
90
|
+
|
|
85
91
|
return (
|
|
86
92
|
<>
|
|
87
93
|
<WrappedComponent
|
|
@@ -105,6 +111,7 @@ const withObjectBrowser = (WrappedComponent) =>
|
|
|
105
111
|
: this.props.data
|
|
106
112
|
}
|
|
107
113
|
contextURL={getBaseUrl(contextURL)}
|
|
114
|
+
initialPath={initialPath}
|
|
108
115
|
closeObjectBrowser={this.closeObjectBrowser}
|
|
109
116
|
mode={this.state.mode}
|
|
110
117
|
onSelectItem={this.state.onSelectItem}
|
|
@@ -84,6 +84,7 @@ class ObjectBrowserBody extends Component {
|
|
|
84
84
|
onSelectItem: PropTypes.func,
|
|
85
85
|
dataName: PropTypes.string,
|
|
86
86
|
maximumSelectionSize: PropTypes.number,
|
|
87
|
+
initialPath: PropTypes.string,
|
|
87
88
|
contextURL: PropTypes.string,
|
|
88
89
|
searchableTypes: PropTypes.arrayOf(PropTypes.string),
|
|
89
90
|
onlyFolderishSelectable: PropTypes.bool,
|
|
@@ -113,18 +114,21 @@ class ObjectBrowserBody extends Component {
|
|
|
113
114
|
*/
|
|
114
115
|
constructor(props) {
|
|
115
116
|
super(props);
|
|
117
|
+
const defaultMultiplePath = props.initialPath || '/';
|
|
116
118
|
this.state = {
|
|
117
119
|
currentFolder:
|
|
118
|
-
this.props.mode === 'multiple'
|
|
120
|
+
this.props.mode === 'multiple'
|
|
121
|
+
? defaultMultiplePath
|
|
122
|
+
: this.props.contextURL || '/',
|
|
119
123
|
currentImageFolder:
|
|
120
124
|
this.props.mode === 'multiple'
|
|
121
|
-
?
|
|
125
|
+
? defaultMultiplePath
|
|
122
126
|
: this.props.mode === 'image' && this.props.data?.url
|
|
123
127
|
? getParentURL(this.props.data.url)
|
|
124
128
|
: '/',
|
|
125
129
|
currentLinkFolder:
|
|
126
130
|
this.props.mode === 'multiple'
|
|
127
|
-
?
|
|
131
|
+
? defaultMultiplePath
|
|
128
132
|
: this.props.mode === 'link' && this.props.data?.href
|
|
129
133
|
? getParentURL(this.props.data.href)
|
|
130
134
|
: '/',
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import configureStore from 'redux-mock-store';
|
|
4
|
+
import { Provider } from 'react-intl-redux';
|
|
5
|
+
import ObjectBrowserBody from './ObjectBrowserBody';
|
|
6
|
+
|
|
7
|
+
const mockStore = configureStore();
|
|
8
|
+
|
|
9
|
+
const baseState = {
|
|
10
|
+
search: { subrequests: {} },
|
|
11
|
+
intl: { locale: 'en', messages: {} },
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const baseProps = {
|
|
15
|
+
block: 'test-block',
|
|
16
|
+
data: {},
|
|
17
|
+
closeObjectBrowser: () => {},
|
|
18
|
+
onChangeBlock: () => {},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const getInitialSearchPath = (actions) => {
|
|
22
|
+
const action = actions.find((a) => a.type === 'SEARCH_CONTENT');
|
|
23
|
+
return action?.request?.path?.split('/@search')[0];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
describe('ObjectBrowserBody', () => {
|
|
27
|
+
it('uses initialPath as the default folder when mode=multiple', () => {
|
|
28
|
+
const store = mockStore(baseState);
|
|
29
|
+
render(
|
|
30
|
+
<Provider store={store}>
|
|
31
|
+
<ObjectBrowserBody
|
|
32
|
+
{...baseProps}
|
|
33
|
+
mode="multiple"
|
|
34
|
+
initialPath="/company/team"
|
|
35
|
+
/>
|
|
36
|
+
</Provider>,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
expect(getInitialSearchPath(store.getActions())).toBe('/company/team');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('defaults to root when mode=multiple and initialPath is not provided', () => {
|
|
43
|
+
const store = mockStore(baseState);
|
|
44
|
+
render(
|
|
45
|
+
<Provider store={store}>
|
|
46
|
+
<ObjectBrowserBody {...baseProps} mode="multiple" />
|
|
47
|
+
</Provider>,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(getInitialSearchPath(store.getActions())).toBe('/');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
+
import { defineMessages, useIntl } from 'react-intl';
|
|
3
4
|
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
4
5
|
|
|
5
6
|
import successSVG from '@plone/volto/icons/ready.svg';
|
|
@@ -7,7 +8,28 @@ import infoSVG from '@plone/volto/icons/info.svg';
|
|
|
7
8
|
import errorSVG from '@plone/volto/icons/error.svg';
|
|
8
9
|
import warningSVG from '@plone/volto/icons/warning.svg';
|
|
9
10
|
|
|
11
|
+
const messages = defineMessages({
|
|
12
|
+
success: {
|
|
13
|
+
id: 'toast_type_success',
|
|
14
|
+
defaultMessage: 'Success',
|
|
15
|
+
},
|
|
16
|
+
error: {
|
|
17
|
+
id: 'toast_type_error',
|
|
18
|
+
defaultMessage: 'Error',
|
|
19
|
+
},
|
|
20
|
+
warning: {
|
|
21
|
+
id: 'toast_type_warning',
|
|
22
|
+
defaultMessage: 'Warning',
|
|
23
|
+
},
|
|
24
|
+
info: {
|
|
25
|
+
id: 'toast_type_info',
|
|
26
|
+
defaultMessage: 'Information',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
10
30
|
const Toast = (props) => {
|
|
31
|
+
const intl = useIntl();
|
|
32
|
+
|
|
11
33
|
function getIcon(props) {
|
|
12
34
|
if (props.info) {
|
|
13
35
|
return infoSVG;
|
|
@@ -22,12 +44,24 @@ const Toast = (props) => {
|
|
|
22
44
|
}
|
|
23
45
|
}
|
|
24
46
|
|
|
47
|
+
function getTypeLabel(props) {
|
|
48
|
+
if (props.error) return intl.formatMessage(messages.error);
|
|
49
|
+
if (props.warning) return intl.formatMessage(messages.warning);
|
|
50
|
+
if (props.info) return intl.formatMessage(messages.info);
|
|
51
|
+
if (props.success) return intl.formatMessage(messages.success);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
25
55
|
const { title, content } = props;
|
|
56
|
+
const typeLabel = getTypeLabel(props);
|
|
26
57
|
|
|
27
58
|
return (
|
|
28
59
|
<>
|
|
29
|
-
<Icon name={getIcon(props)} size="18px" />
|
|
60
|
+
<Icon name={getIcon(props)} size="18px" ariaHidden={true} />
|
|
30
61
|
<div className="toast-inner-content">
|
|
62
|
+
{typeLabel && (
|
|
63
|
+
<span className="visually-hidden-volto">{typeLabel}</span>
|
|
64
|
+
)}
|
|
31
65
|
{title && <h4>{title}</h4>}
|
|
32
66
|
<div>{content}</div>
|
|
33
67
|
</div>
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import renderer from 'react-test-renderer';
|
|
3
|
+
import { IntlProvider } from 'react-intl';
|
|
3
4
|
import Toast from './Toast';
|
|
4
5
|
|
|
5
6
|
test('renders a Toast info component', () => {
|
|
6
7
|
const component = renderer.create(
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
<IntlProvider locale="en">
|
|
9
|
+
<Toast
|
|
10
|
+
info
|
|
11
|
+
title="I'm a title"
|
|
12
|
+
content="This is the content, lorem ipsum"
|
|
13
|
+
/>
|
|
14
|
+
</IntlProvider>,
|
|
12
15
|
);
|
|
13
16
|
const json = component.toJSON();
|
|
14
17
|
expect(json).toMatchSnapshot();
|
|
@@ -315,7 +315,8 @@ class ArrayWidget extends Component {
|
|
|
315
315
|
// small fix for https://github.com/clauderic/react-sortable-hoc/pull/352:
|
|
316
316
|
getHelperDimensions={({ node }) => node.getBoundingClientRect()}
|
|
317
317
|
id={`field-${this.props.id}`}
|
|
318
|
-
|
|
318
|
+
fieldTitle={this.props.title}
|
|
319
|
+
aria-label={this.props.title || undefined}
|
|
319
320
|
key={this.props.id}
|
|
320
321
|
isDisabled={this.props.disabled || this.props.isDisabled}
|
|
321
322
|
className="react-select-container"
|
|
@@ -384,7 +385,7 @@ class ArrayWidget extends Component {
|
|
|
384
385
|
)
|
|
385
386
|
)
|
|
386
387
|
}
|
|
387
|
-
isClearable
|
|
388
|
+
isClearable={!this.props.required}
|
|
388
389
|
isMulti
|
|
389
390
|
/>
|
|
390
391
|
</FormFieldWrapper>
|
|
@@ -2,9 +2,14 @@
|
|
|
2
2
|
* FormFieldWrapper component.
|
|
3
3
|
* @module components/manage/Widgets/FormFieldWrapper
|
|
4
4
|
*/
|
|
5
|
-
import React, {
|
|
5
|
+
import React, {
|
|
6
|
+
Children,
|
|
7
|
+
Component,
|
|
8
|
+
isValidElement,
|
|
9
|
+
cloneElement,
|
|
10
|
+
} from 'react';
|
|
6
11
|
import PropTypes from 'prop-types';
|
|
7
|
-
import { Form, Grid, Icon as IconOld, Label } from 'semantic-ui-react';
|
|
12
|
+
import { Form, Grid, Icon as IconOld, Input, Label } from 'semantic-ui-react';
|
|
8
13
|
import map from 'lodash/map';
|
|
9
14
|
import cx from 'classnames';
|
|
10
15
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
@@ -103,7 +108,15 @@ class FormFieldWrapper extends Component {
|
|
|
103
108
|
|
|
104
109
|
const wdg = (
|
|
105
110
|
<>
|
|
106
|
-
{this.props.children
|
|
111
|
+
{Children.map(this.props.children, (child) => {
|
|
112
|
+
if (isValidElement(child) && required && child.type === Input) {
|
|
113
|
+
return cloneElement(child, {
|
|
114
|
+
'aria-required': true,
|
|
115
|
+
'aria-invalid': !!(error && error.length > 0),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return child;
|
|
119
|
+
})}
|
|
107
120
|
|
|
108
121
|
<div aria-live="polite" aria-atomic="true">
|
|
109
122
|
{map(error, (message) => (
|
|
@@ -307,6 +307,7 @@ export class ObjectBrowserWidgetComponent extends Component {
|
|
|
307
307
|
this.props.openObjectBrowser({
|
|
308
308
|
mode: this.props.mode,
|
|
309
309
|
currentPath: this.props.initialPath || this.props.location.pathname,
|
|
310
|
+
initialPath: this.props.initialPath,
|
|
310
311
|
propDataName: 'value',
|
|
311
312
|
onSelectItem: (url, item) => {
|
|
312
313
|
this.onChange(item);
|
|
@@ -7,10 +7,6 @@ const messages = defineMessages({
|
|
|
7
7
|
id: 'Criteria',
|
|
8
8
|
defaultMessage: 'Criteria',
|
|
9
9
|
},
|
|
10
|
-
depth: {
|
|
11
|
-
id: 'Depth',
|
|
12
|
-
defaultMessage: 'Depth',
|
|
13
|
-
},
|
|
14
10
|
SortOn: {
|
|
15
11
|
id: 'Sort on',
|
|
16
12
|
defaultMessage: 'Sort on',
|
|
@@ -38,16 +34,7 @@ export const objectSchema = ({ intl, isDisabled, value }) => ({
|
|
|
38
34
|
{
|
|
39
35
|
id: 'default',
|
|
40
36
|
title: 'Default',
|
|
41
|
-
fields: [
|
|
42
|
-
'query',
|
|
43
|
-
...(value?.query?.filter((q) => q.i === 'path').length > 0
|
|
44
|
-
? ['depth']
|
|
45
|
-
: []),
|
|
46
|
-
'sort_on',
|
|
47
|
-
'sort_order_boolean',
|
|
48
|
-
'limit',
|
|
49
|
-
'b_size',
|
|
50
|
-
],
|
|
37
|
+
fields: ['query', 'sort_on', 'sort_order_boolean', 'limit', 'b_size'],
|
|
51
38
|
},
|
|
52
39
|
],
|
|
53
40
|
properties: {
|
|
@@ -55,10 +42,6 @@ export const objectSchema = ({ intl, isDisabled, value }) => ({
|
|
|
55
42
|
title: intl.formatMessage(messages.Criteria),
|
|
56
43
|
widget: 'query',
|
|
57
44
|
},
|
|
58
|
-
depth: {
|
|
59
|
-
title: intl.formatMessage(messages.depth),
|
|
60
|
-
type: 'number',
|
|
61
|
-
},
|
|
62
45
|
sort_on: {
|
|
63
46
|
title: intl.formatMessage(messages.SortOn),
|
|
64
47
|
widget: 'query_sort_on',
|