@plone/volto 18.10.1 → 18.11.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 +53 -0
- package/README.md +3 -3
- package/locales/ca/LC_MESSAGES/volto.po +46 -0
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +46 -0
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +46 -0
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +46 -0
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +46 -0
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +46 -0
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +46 -0
- package/locales/fr.json +1 -1
- package/locales/hi/LC_MESSAGES/volto.po +46 -0
- package/locales/hi.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +46 -0
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +46 -0
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +46 -0
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +46 -0
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +47 -1
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +46 -0
- package/locales/ro.json +1 -1
- package/locales/ru/LC_MESSAGES/volto.po +5243 -0
- package/locales/ru.json +1 -0
- package/locales/volto.pot +47 -1
- package/locales/zh_CN/LC_MESSAGES/volto.po +46 -0
- package/locales/zh_CN.json +1 -1
- package/package.json +4 -4
- package/razzle.config.js +1 -1
- package/src/components/index.js +2 -0
- package/src/components/manage/Blocks/Block/BlocksForm.jsx +5 -1
- package/src/components/manage/Blocks/Search/components/SearchInput.jsx +11 -1
- package/src/components/manage/DragDropList/DragDropList.jsx +78 -33
- package/src/components/manage/Form/Field.jsx +38 -29
- package/src/components/manage/Form/Form.jsx +141 -69
- package/src/components/manage/Form/ModalForm.jsx +29 -0
- package/src/components/manage/Sidebar/ObjectBrowserNav.jsx +1 -1
- package/src/components/manage/Sidebar/ObjectBrowserNav.test.jsx +34 -13
- package/src/components/manage/Widgets/CheckboxGroupWidget.jsx +214 -0
- package/src/components/manage/Widgets/CheckboxGroupWidget.stories.jsx +39 -0
- package/src/components/manage/Widgets/CheckboxGroupWidget.test.jsx +45 -0
- package/src/components/manage/Widgets/FileWidget.jsx +43 -2
- package/src/components/manage/Widgets/HiddenWidget.jsx +114 -0
- package/src/components/manage/Widgets/HiddenWidget.stories.jsx +20 -0
- package/src/components/manage/Widgets/HiddenWidget.test.jsx +32 -0
- package/src/components/manage/Widgets/ImageWidget.jsx +3 -0
- package/src/components/manage/Widgets/RadioGroupWidget.jsx +207 -0
- package/src/components/manage/Widgets/RadioGroupWidget.stories.jsx +39 -0
- package/src/components/manage/Widgets/RadioGroupWidget.test.jsx +46 -0
- package/src/components/manage/Widgets/SchemaWidget.jsx +806 -320
- package/src/components/manage/Widgets/SelectWidget.jsx +38 -2
- package/src/components/manage/Widgets/StaticTextWidget.jsx +34 -0
- package/src/components/manage/Widgets/StaticTextWidget.stories.jsx +20 -0
- package/src/components/manage/Widgets/StaticTextWidget.test.jsx +25 -0
- package/src/components/manage/Widgets/TimeWidget.jsx +96 -0
- package/src/components/manage/Widgets/TimeWidget.stories.jsx +22 -0
- package/src/components/manage/Widgets/TimeWidget.test.jsx +35 -0
- package/src/components/manage/Widgets/index.tsx +35 -0
- package/src/components/theme/ContactForm/ContactForm.jsx +7 -4
- package/src/components/theme/Widgets/HiddenWidget.jsx +2 -0
- package/src/components/theme/Widgets/HiddenWidget.stories.jsx +25 -0
- package/src/components/theme/Widgets/HiddenWidget.test.jsx +11 -0
- package/src/components/theme/Widgets/StaticTextWidget.jsx +16 -0
- package/src/components/theme/Widgets/StaticTextWidget.stories.jsx +29 -0
- package/src/components/theme/Widgets/StaticTextWidget.test.jsx +32 -0
- package/src/config/Blocks.jsx +2 -0
- package/src/config/Widgets.jsx +14 -0
- package/src/constants/Languages.cjs +1 -0
- package/src/helpers/FormValidation/FormValidation.jsx +4 -0
- package/src/helpers/FormValidation/FormValidation.test.js +147 -31
- package/src/helpers/FormValidation/validators.ts +4 -1
- package/src/helpers/Utils/Utils.jsx +14 -2
- package/theme/themes/pastanaga/collections/form.overrides +4 -0
- package/theme/themes/pastanaga/elements/input.overrides +7 -0
- package/theme/themes/pastanaga/extras/sidebar.less +2 -0
- package/types/components/index.d.ts +1 -1
- package/types/components/manage/Widgets/CheckboxGroupWidget.d.ts +6 -0
- package/types/components/manage/Widgets/CheckboxGroupWidget.stories.d.ts +15 -0
- package/types/components/manage/Widgets/CheckboxGroupWidget.test.d.ts +1 -0
- package/types/components/manage/Widgets/HiddenWidget.d.ts +54 -0
- package/types/components/manage/Widgets/HiddenWidget.stories.d.ts +9 -0
- package/types/components/manage/Widgets/HiddenWidget.test.d.ts +1 -0
- package/types/components/manage/Widgets/RadioGroupWidget.d.ts +6 -0
- package/types/components/manage/Widgets/RadioGroupWidget.stories.d.ts +15 -0
- package/types/components/manage/Widgets/RadioGroupWidget.test.d.ts +1 -0
- package/types/components/manage/Widgets/StaticTextWidget.d.ts +18 -0
- package/types/components/manage/Widgets/StaticTextWidget.stories.d.ts +9 -0
- package/types/components/manage/Widgets/StaticTextWidget.test.d.ts +1 -0
- package/types/components/manage/Widgets/TimeWidget.d.ts +2 -0
- package/types/components/manage/Widgets/TimeWidget.stories.d.ts +8 -0
- package/types/components/manage/Widgets/TimeWidget.test.d.ts +1 -0
- package/types/components/manage/Widgets/index.d.ts +5 -0
- package/types/components/theme/Widgets/HiddenWidget.d.ts +6 -0
- package/types/components/theme/Widgets/HiddenWidget.stories.d.ts +8 -0
- package/types/components/theme/Widgets/HiddenWidget.test.d.ts +1 -0
- package/types/components/theme/Widgets/StaticTextWidget.d.ts +5 -0
- package/types/components/theme/Widgets/StaticTextWidget.stories.d.ts +8 -0
- package/types/components/theme/Widgets/StaticTextWidget.test.d.ts +1 -0
- package/types/config/Widgets.d.ts +14 -0
- package/types/constants/Languages.d.cts +1 -0
- package/types/helpers/FormValidation/validators.d.ts +1 -1
- package/types/helpers/Utils/Utils.d.ts +1 -1
|
@@ -7,7 +7,9 @@ import React, { Component } from 'react';
|
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
8
|
import { connect } from 'react-redux';
|
|
9
9
|
import { compose } from 'redux';
|
|
10
|
+
import filter from 'lodash/filter';
|
|
10
11
|
import map from 'lodash/map';
|
|
12
|
+
import sortBy from 'lodash/sortBy';
|
|
11
13
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
12
14
|
import {
|
|
13
15
|
getVocabFromHint,
|
|
@@ -95,6 +97,7 @@ class SelectWidget extends Component {
|
|
|
95
97
|
title: PropTypes.string.isRequired,
|
|
96
98
|
description: PropTypes.string,
|
|
97
99
|
required: PropTypes.bool,
|
|
100
|
+
filterChoices: PropTypes.arrayOf(PropTypes.string),
|
|
98
101
|
error: PropTypes.arrayOf(PropTypes.string),
|
|
99
102
|
getVocabulary: PropTypes.func.isRequired,
|
|
100
103
|
getVocabularyTokenTitle: PropTypes.func.isRequired,
|
|
@@ -124,6 +127,7 @@ class SelectWidget extends Component {
|
|
|
124
127
|
customOptionStyling: PropTypes.any,
|
|
125
128
|
isMulti: PropTypes.bool,
|
|
126
129
|
placeholder: PropTypes.string,
|
|
130
|
+
sort: PropTypes.bool,
|
|
127
131
|
};
|
|
128
132
|
|
|
129
133
|
/**
|
|
@@ -134,6 +138,7 @@ class SelectWidget extends Component {
|
|
|
134
138
|
static defaultProps = {
|
|
135
139
|
description: null,
|
|
136
140
|
required: false,
|
|
141
|
+
filterChoices: null,
|
|
137
142
|
items: {
|
|
138
143
|
vocabulary: null,
|
|
139
144
|
},
|
|
@@ -150,6 +155,7 @@ class SelectWidget extends Component {
|
|
|
150
155
|
onDelete: null,
|
|
151
156
|
noValueOption: true,
|
|
152
157
|
customOptionStyling: null,
|
|
158
|
+
sort: false,
|
|
153
159
|
};
|
|
154
160
|
|
|
155
161
|
/**
|
|
@@ -189,8 +195,15 @@ class SelectWidget extends Component {
|
|
|
189
195
|
* @returns {string} Markup for the component.
|
|
190
196
|
*/
|
|
191
197
|
render() {
|
|
192
|
-
const {
|
|
193
|
-
|
|
198
|
+
const {
|
|
199
|
+
id,
|
|
200
|
+
choices,
|
|
201
|
+
value,
|
|
202
|
+
intl,
|
|
203
|
+
onChange,
|
|
204
|
+
filterChoices,
|
|
205
|
+
additionalChoices,
|
|
206
|
+
} = this.props;
|
|
194
207
|
// Make sure that both disabled and isDisabled (from the DX layout feat work)
|
|
195
208
|
const disabled = this.props.disabled || this.props.isDisabled;
|
|
196
209
|
const Select = this.props.reactSelect.default;
|
|
@@ -217,6 +230,29 @@ class SelectWidget extends Component {
|
|
|
217
230
|
: []),
|
|
218
231
|
];
|
|
219
232
|
|
|
233
|
+
if (additionalChoices) {
|
|
234
|
+
options = [
|
|
235
|
+
...(options || []),
|
|
236
|
+
...map(additionalChoices, (choice) => ({
|
|
237
|
+
value: choice.value,
|
|
238
|
+
label: intl.formatMessage({
|
|
239
|
+
id: choice.value,
|
|
240
|
+
defaultMessage: choice.label,
|
|
241
|
+
}),
|
|
242
|
+
})),
|
|
243
|
+
];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (filterChoices) {
|
|
247
|
+
options = filter(options, (item) => filterChoices.includes(item.value));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (this.props.sort) {
|
|
251
|
+
options = sortBy(options, ['label']);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const normalizedValue = normalizeValue(options, value, intl);
|
|
255
|
+
|
|
220
256
|
const isMulti = this.props.isMulti
|
|
221
257
|
? this.props.isMulti
|
|
222
258
|
: id === 'roles' || id === 'groups' || this.props.type === 'array';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
|
|
3
|
+
import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
|
|
4
|
+
|
|
5
|
+
const StaticTextWidget = (props) => {
|
|
6
|
+
const { id, value } = props;
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<FormFieldWrapper {...props} className="text" columns={1}>
|
|
10
|
+
<div id={id} className="wrapper">
|
|
11
|
+
<p
|
|
12
|
+
dangerouslySetInnerHTML={{
|
|
13
|
+
__html: value?.data || '',
|
|
14
|
+
}}
|
|
15
|
+
/>
|
|
16
|
+
</div>
|
|
17
|
+
</FormFieldWrapper>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default StaticTextWidget;
|
|
22
|
+
|
|
23
|
+
StaticTextWidget.propTypes = {
|
|
24
|
+
id: PropTypes.string.isRequired,
|
|
25
|
+
value: PropTypes.string,
|
|
26
|
+
minLength: PropTypes.number,
|
|
27
|
+
maxLength: PropTypes.number,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
StaticTextWidget.defaultProps = {
|
|
31
|
+
value: null,
|
|
32
|
+
minLength: null,
|
|
33
|
+
maxLength: null,
|
|
34
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import StaticTextWidget from './StaticTextWidget';
|
|
2
|
+
import WidgetStory from './story';
|
|
3
|
+
|
|
4
|
+
export const StaticText = WidgetStory.bind({
|
|
5
|
+
props: { id: 'text', title: 'Text' },
|
|
6
|
+
widget: StaticTextWidget,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
title: 'Edit Widgets/Static Text',
|
|
11
|
+
component: StaticTextWidget,
|
|
12
|
+
decorators: [
|
|
13
|
+
(Story) => (
|
|
14
|
+
<div className="ui segment form attached" style={{ width: '400px' }}>
|
|
15
|
+
<Story />
|
|
16
|
+
</div>
|
|
17
|
+
),
|
|
18
|
+
],
|
|
19
|
+
argTypes: {},
|
|
20
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer from 'react-test-renderer';
|
|
3
|
+
import configureStore from 'redux-mock-store';
|
|
4
|
+
import { Provider } from 'react-intl-redux';
|
|
5
|
+
|
|
6
|
+
import StaticTextWidget from './StaticTextWidget';
|
|
7
|
+
|
|
8
|
+
const mockStore = configureStore();
|
|
9
|
+
|
|
10
|
+
test('renders a text widget component', () => {
|
|
11
|
+
const store = mockStore({
|
|
12
|
+
intl: {
|
|
13
|
+
locale: 'en',
|
|
14
|
+
messages: {},
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const component = renderer.create(
|
|
19
|
+
<Provider store={store}>
|
|
20
|
+
<StaticTextWidget id="my-field" />
|
|
21
|
+
</Provider>,
|
|
22
|
+
);
|
|
23
|
+
const json = component.toJSON();
|
|
24
|
+
expect(json).toMatchSnapshot();
|
|
25
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { defineMessages, useIntl } from 'react-intl';
|
|
4
|
+
import loadable from '@loadable/component';
|
|
5
|
+
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
6
|
+
import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
|
|
7
|
+
import { toBackendLang } from '@plone/volto/helpers/Utils/Utils';
|
|
8
|
+
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
9
|
+
|
|
10
|
+
import clearSVG from '@plone/volto/icons/clear.svg';
|
|
11
|
+
|
|
12
|
+
import 'rc-time-picker/assets/index.css';
|
|
13
|
+
|
|
14
|
+
const TimePicker = loadable(() => import('rc-time-picker'));
|
|
15
|
+
|
|
16
|
+
const messages = defineMessages({
|
|
17
|
+
time: {
|
|
18
|
+
id: 'Time',
|
|
19
|
+
defaultMessage: 'Time',
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const TimeWidgetComponent = (props) => {
|
|
24
|
+
const { id, resettable, moment, value, onChange, isDisabled } = props;
|
|
25
|
+
|
|
26
|
+
const intl = useIntl();
|
|
27
|
+
const lang = intl.locale;
|
|
28
|
+
|
|
29
|
+
const onTimeChange = (time) => {
|
|
30
|
+
if (time) {
|
|
31
|
+
onChange(id, time.format('HH:mm'));
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const onResetTime = () => {
|
|
36
|
+
onChange(id, null);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<FormFieldWrapper {...props}>
|
|
41
|
+
<div className="date-time-widget-wrapper">
|
|
42
|
+
<div className="ui input time-input">
|
|
43
|
+
<TimePicker
|
|
44
|
+
disabled={isDisabled}
|
|
45
|
+
defaultValue={moment.default()}
|
|
46
|
+
value={value ? moment.default(value, 'HH:mm') : null}
|
|
47
|
+
onChange={onTimeChange}
|
|
48
|
+
allowEmpty={false}
|
|
49
|
+
showSecond={false}
|
|
50
|
+
use12Hours={lang === 'en'}
|
|
51
|
+
id={id}
|
|
52
|
+
format={moment.default
|
|
53
|
+
.localeData(toBackendLang(lang))
|
|
54
|
+
.longDateFormat('LT')}
|
|
55
|
+
placeholder={intl.formatMessage(messages.time)}
|
|
56
|
+
focusOnOpen
|
|
57
|
+
placement="bottomRight"
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
{resettable && (
|
|
61
|
+
<button
|
|
62
|
+
type="button"
|
|
63
|
+
disabled={isDisabled || !value}
|
|
64
|
+
onClick={onResetTime}
|
|
65
|
+
className="item ui noborder button"
|
|
66
|
+
>
|
|
67
|
+
<Icon name={clearSVG} size="24px" className="close" />
|
|
68
|
+
</button>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
</FormFieldWrapper>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
TimeWidgetComponent.propTypes = {
|
|
76
|
+
id: PropTypes.string.isRequired,
|
|
77
|
+
title: PropTypes.string.isRequired,
|
|
78
|
+
description: PropTypes.string,
|
|
79
|
+
required: PropTypes.bool,
|
|
80
|
+
error: PropTypes.arrayOf(PropTypes.string),
|
|
81
|
+
value: PropTypes.string,
|
|
82
|
+
onChange: PropTypes.func.isRequired,
|
|
83
|
+
resettable: PropTypes.bool,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
TimeWidgetComponent.defaultProps = {
|
|
87
|
+
description: null,
|
|
88
|
+
required: false,
|
|
89
|
+
error: [],
|
|
90
|
+
dateOnly: false,
|
|
91
|
+
noPastDates: false,
|
|
92
|
+
value: null,
|
|
93
|
+
resettable: true,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export default injectLazyLibs(['reactDates', 'moment'])(TimeWidgetComponent);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TimeWidgetComponent } from './TimeWidget';
|
|
3
|
+
import TimeWidget from './TimeWidget';
|
|
4
|
+
import WidgetStory from './story';
|
|
5
|
+
|
|
6
|
+
export const Time = WidgetStory.bind({
|
|
7
|
+
props: { id: 'time', title: 'Time', block: 'block' },
|
|
8
|
+
widget: TimeWidget,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
title: 'Edit Widgets/Time',
|
|
13
|
+
component: TimeWidgetComponent,
|
|
14
|
+
decorators: [
|
|
15
|
+
(Story) => (
|
|
16
|
+
<div className="ui segment form attached" style={{ width: '400px' }}>
|
|
17
|
+
<Story />
|
|
18
|
+
</div>
|
|
19
|
+
),
|
|
20
|
+
],
|
|
21
|
+
argTypes: {},
|
|
22
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Provider } from 'react-intl-redux';
|
|
3
|
+
import configureStore from 'redux-mock-store';
|
|
4
|
+
import TimeWidget from './TimeWidget';
|
|
5
|
+
import { waitFor, render, screen } from '@testing-library/react';
|
|
6
|
+
|
|
7
|
+
const mockStore = configureStore();
|
|
8
|
+
|
|
9
|
+
jest.mock('@plone/volto/helpers/Loadable/Loadable');
|
|
10
|
+
beforeAll(
|
|
11
|
+
async () =>
|
|
12
|
+
await require('@plone/volto/helpers/Loadable/Loadable').__setLoadables(),
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
test('renders a time widget component', async () => {
|
|
16
|
+
const store = mockStore({
|
|
17
|
+
intl: {
|
|
18
|
+
locale: 'en',
|
|
19
|
+
messages: {},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
const { container } = render(
|
|
23
|
+
<Provider store={store}>
|
|
24
|
+
<TimeWidget
|
|
25
|
+
id="my-field"
|
|
26
|
+
title="My field"
|
|
27
|
+
fieldSet="default"
|
|
28
|
+
onChange={() => {}}
|
|
29
|
+
value={'12:00'}
|
|
30
|
+
/>
|
|
31
|
+
</Provider>,
|
|
32
|
+
);
|
|
33
|
+
await waitFor(() => screen.getByText(/My field/));
|
|
34
|
+
expect(container).toMatchSnapshot();
|
|
35
|
+
});
|
|
@@ -28,6 +28,13 @@ export const CheckboxWidget = loadable(
|
|
|
28
28
|
),
|
|
29
29
|
);
|
|
30
30
|
|
|
31
|
+
export const CheckboxGroupWidget = loadable(
|
|
32
|
+
() =>
|
|
33
|
+
import(
|
|
34
|
+
/* webpackChunkName: "Widgets" */ '@plone/volto/components/manage/Widgets/CheckboxGroupWidget'
|
|
35
|
+
),
|
|
36
|
+
);
|
|
37
|
+
|
|
31
38
|
export const FileWidget = loadable(
|
|
32
39
|
() =>
|
|
33
40
|
import(
|
|
@@ -35,6 +42,13 @@ export const FileWidget = loadable(
|
|
|
35
42
|
),
|
|
36
43
|
);
|
|
37
44
|
|
|
45
|
+
export const HiddenWidget = loadable(
|
|
46
|
+
() =>
|
|
47
|
+
import(
|
|
48
|
+
/* webpackChunkName: "Widgets" */ '@plone/volto/components/manage/Widgets/HiddenWidget'
|
|
49
|
+
),
|
|
50
|
+
);
|
|
51
|
+
|
|
38
52
|
export const IdWidget = loadable(
|
|
39
53
|
() =>
|
|
40
54
|
import(
|
|
@@ -70,6 +84,13 @@ export const QuerystringWidget = loadable(
|
|
|
70
84
|
),
|
|
71
85
|
);
|
|
72
86
|
|
|
87
|
+
export const RadioGroupWidget = loadable(
|
|
88
|
+
() =>
|
|
89
|
+
import(
|
|
90
|
+
/* webpackChunkName: "Widgets" */ '@plone/volto/components/manage/Widgets/RadioGroupWidget'
|
|
91
|
+
),
|
|
92
|
+
);
|
|
93
|
+
|
|
73
94
|
export const SchemaWidget = loadable(
|
|
74
95
|
() =>
|
|
75
96
|
import(
|
|
@@ -84,6 +105,13 @@ export const SelectWidget = loadable(
|
|
|
84
105
|
),
|
|
85
106
|
);
|
|
86
107
|
|
|
108
|
+
export const StaticTextWidget = loadable(
|
|
109
|
+
() =>
|
|
110
|
+
import(
|
|
111
|
+
/* webpackChunkName: "Widgets" */ '@plone/volto/components/manage/Widgets/StaticTextWidget'
|
|
112
|
+
),
|
|
113
|
+
);
|
|
114
|
+
|
|
87
115
|
export const TextareaWidget = loadable(
|
|
88
116
|
() =>
|
|
89
117
|
import(
|
|
@@ -217,6 +245,13 @@ export const DatetimeWidget = loadable(
|
|
|
217
245
|
),
|
|
218
246
|
);
|
|
219
247
|
|
|
248
|
+
export const TimeWidget = loadable(
|
|
249
|
+
() =>
|
|
250
|
+
import(
|
|
251
|
+
/* webpackChunkName: "Widgets" */ '@plone/volto/components/manage/Widgets/TimeWidget'
|
|
252
|
+
),
|
|
253
|
+
);
|
|
254
|
+
|
|
220
255
|
export const RecurrenceWidget = loadable(
|
|
221
256
|
() =>
|
|
222
257
|
import(
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect } from 'react';
|
|
2
2
|
import { createPortal } from 'react-dom';
|
|
3
|
-
import { Container, Message
|
|
3
|
+
import { Container, Message } from 'semantic-ui-react';
|
|
4
|
+
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
4
5
|
import { defineMessages, useIntl } from 'react-intl';
|
|
5
6
|
import { Link, useHistory, useLocation } from 'react-router-dom';
|
|
6
7
|
import { toast } from 'react-toastify';
|
|
@@ -15,6 +16,8 @@ import Helmet from '@plone/volto/helpers/Helmet/Helmet';
|
|
|
15
16
|
import { usePrevious } from '@plone/volto/helpers/Utils/usePrevious';
|
|
16
17
|
import { useClient } from '@plone/volto/hooks/client/useClient';
|
|
17
18
|
|
|
19
|
+
import backSVG from '@plone/volto/icons/back.svg';
|
|
20
|
+
|
|
18
21
|
const messages = defineMessages({
|
|
19
22
|
send: {
|
|
20
23
|
id: 'Send',
|
|
@@ -161,9 +164,9 @@ const ContactFormComponent = () => {
|
|
|
161
164
|
inner={
|
|
162
165
|
<Link to={`${getBaseUrl(pathname)}`} className="item">
|
|
163
166
|
<Icon
|
|
164
|
-
name=
|
|
165
|
-
|
|
166
|
-
|
|
167
|
+
name={backSVG}
|
|
168
|
+
className="contents circled"
|
|
169
|
+
size="30px"
|
|
167
170
|
title={intl.formatMessage(messages.back)}
|
|
168
171
|
/>
|
|
169
172
|
</Link>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import HiddenWidget from './HiddenWidget';
|
|
3
|
+
import Wrapper from '@plone/volto/storybook';
|
|
4
|
+
|
|
5
|
+
const HiddenWidgetComponent = ({ children, className, value }) => {
|
|
6
|
+
return (
|
|
7
|
+
<Wrapper location={{ pathname: '/folder2/folder21/doc212' }}>
|
|
8
|
+
<div className="ui segment form attached" style={{ width: '400px' }}>
|
|
9
|
+
<HiddenWidget value={value} children={children} className={className} />
|
|
10
|
+
</div>
|
|
11
|
+
</Wrapper>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const Text = HiddenWidgetComponent.bind({});
|
|
16
|
+
Text.args = {
|
|
17
|
+
value: 'Hidden widget render',
|
|
18
|
+
className: '',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default {
|
|
22
|
+
title: 'View Widgets/Hidden',
|
|
23
|
+
component: HiddenWidget,
|
|
24
|
+
argTypes: {},
|
|
25
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer from 'react-test-renderer';
|
|
3
|
+
import HiddenWidget from './HiddenWidget';
|
|
4
|
+
|
|
5
|
+
describe('HiddenWidget', () => {
|
|
6
|
+
it('renders an empty hidden view widget component', () => {
|
|
7
|
+
const component = renderer.create(<HiddenWidget />);
|
|
8
|
+
const json = component.toJSON();
|
|
9
|
+
expect(json).toMatchSnapshot();
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import cx from 'classnames';
|
|
3
|
+
|
|
4
|
+
const StaticTextWidget = ({ value, className }) =>
|
|
5
|
+
value ? (
|
|
6
|
+
<p
|
|
7
|
+
className={cx(className, 'statictext', 'widget')}
|
|
8
|
+
dangerouslySetInnerHTML={{
|
|
9
|
+
__html: value.data,
|
|
10
|
+
}}
|
|
11
|
+
/>
|
|
12
|
+
) : (
|
|
13
|
+
''
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
export default StaticTextWidget;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import StaticTextWidget from './StaticTextWidget';
|
|
3
|
+
import Wrapper from '@plone/volto/storybook';
|
|
4
|
+
|
|
5
|
+
const StaticTextWidgetComponent = ({ children, className, value }) => {
|
|
6
|
+
return (
|
|
7
|
+
<Wrapper location={{ pathname: '/folder2/folder21/doc212' }}>
|
|
8
|
+
<div className="ui segment form attached" style={{ width: '400px' }}>
|
|
9
|
+
<StaticTextWidget
|
|
10
|
+
value={value}
|
|
11
|
+
children={children}
|
|
12
|
+
className={className}
|
|
13
|
+
/>
|
|
14
|
+
</div>
|
|
15
|
+
</Wrapper>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const StaticText = StaticTextWidgetComponent.bind({});
|
|
20
|
+
StaticTextWidget.args = {
|
|
21
|
+
value: { data: '<p><strong>Hello</strong> <em>world</em></p>' },
|
|
22
|
+
className: '',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default {
|
|
26
|
+
title: 'View Widgets/Static Text',
|
|
27
|
+
component: StaticTextWidget,
|
|
28
|
+
argTypes: {},
|
|
29
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer from 'react-test-renderer';
|
|
3
|
+
import StaticTextWidget from './StaticTextWidget';
|
|
4
|
+
|
|
5
|
+
describe('StaticTextWidget', () => {
|
|
6
|
+
it('renders an empty static text view widget component', () => {
|
|
7
|
+
const component = renderer.create(<StaticTextWidget />);
|
|
8
|
+
const json = component.toJSON();
|
|
9
|
+
expect(json).toMatchSnapshot();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('renders a static text view widget component', () => {
|
|
13
|
+
const component = renderer.create(
|
|
14
|
+
<StaticTextWidget
|
|
15
|
+
className="metadata"
|
|
16
|
+
value={{ data: '<b>Foo bar</b>' }}
|
|
17
|
+
/>,
|
|
18
|
+
);
|
|
19
|
+
const json = component.toJSON();
|
|
20
|
+
expect(json).toMatchSnapshot();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('renders a static text view widget component with children', () => {
|
|
24
|
+
const component = renderer.create(
|
|
25
|
+
<StaticTextWidget className="metadata" value={{ data: '<b>Foo bar</b>' }}>
|
|
26
|
+
{(child) => <strong>{child}</strong>}
|
|
27
|
+
</StaticTextWidget>,
|
|
28
|
+
);
|
|
29
|
+
const json = component.toJSON();
|
|
30
|
+
expect(json).toMatchSnapshot();
|
|
31
|
+
});
|
|
32
|
+
});
|
package/src/config/Blocks.jsx
CHANGED
|
@@ -72,6 +72,7 @@ import { getLeadImageBlockSizes } from '@plone/volto/components/manage/Blocks/Le
|
|
|
72
72
|
// block sidebar schemas (not the Dexterity Layout block settings schemas)
|
|
73
73
|
import ListingBlockSchema from '@plone/volto/components/manage/Blocks/Listing/schema';
|
|
74
74
|
import SearchBlockSchema from '@plone/volto/components/manage/Blocks/Search/schema';
|
|
75
|
+
import VideoBlockSchema from '@plone/volto/components/manage/Blocks/Video/schema';
|
|
75
76
|
|
|
76
77
|
import ToCVariations from '@plone/volto/components/manage/Blocks/ToC/variations';
|
|
77
78
|
|
|
@@ -346,6 +347,7 @@ const blocksConfig = {
|
|
|
346
347
|
view: ViewVideoBlock,
|
|
347
348
|
edit: EditVideoBlock,
|
|
348
349
|
schema: BlockSettingsSchema,
|
|
350
|
+
blockSchema: VideoBlockSchema,
|
|
349
351
|
restricted: false,
|
|
350
352
|
mostUsed: true,
|
|
351
353
|
sidebarTab: 1,
|
package/src/config/Widgets.jsx
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
CheckboxWidget,
|
|
6
6
|
FileWidget,
|
|
7
7
|
IdWidget,
|
|
8
|
+
HiddenWidget,
|
|
8
9
|
PasswordWidget,
|
|
9
10
|
QueryWidget,
|
|
10
11
|
QuerySortOnWidget,
|
|
@@ -27,9 +28,13 @@ import {
|
|
|
27
28
|
VocabularyTermsWidget,
|
|
28
29
|
SelectMetadataWidget,
|
|
29
30
|
SelectAutoComplete,
|
|
31
|
+
StaticTextWidget,
|
|
30
32
|
ColorPickerWidget,
|
|
31
33
|
DatetimeWidget,
|
|
34
|
+
TimeWidget,
|
|
32
35
|
RecurrenceWidget,
|
|
36
|
+
RadioGroupWidget,
|
|
37
|
+
CheckboxGroupWidget,
|
|
33
38
|
} from '@plone/volto/components/manage/Widgets';
|
|
34
39
|
|
|
35
40
|
import ArrayViewWidget from '@plone/volto/components/theme/Widgets/ArrayWidget';
|
|
@@ -51,6 +56,8 @@ import TitleViewWidget from '@plone/volto/components/theme/Widgets/TitleWidget';
|
|
|
51
56
|
import TokenViewWidget from '@plone/volto/components/theme/Widgets/TokenWidget';
|
|
52
57
|
import UrlViewWidget from '@plone/volto/components/theme/Widgets/UrlWidget';
|
|
53
58
|
import ImageWidget from '@plone/volto/components/manage/Widgets/ImageWidget';
|
|
59
|
+
import HiddenViewWidget from '@plone/volto/components/manage/Widgets/HiddenWidget';
|
|
60
|
+
import StaticTextViewWidget from '@plone/volto/components/manage/Widgets/StaticTextWidget';
|
|
54
61
|
|
|
55
62
|
// Widgets mapping
|
|
56
63
|
export const widgetMapping = {
|
|
@@ -67,6 +74,7 @@ export const widgetMapping = {
|
|
|
67
74
|
textarea: TextareaWidget,
|
|
68
75
|
datetime: DatetimeWidget,
|
|
69
76
|
date: DatetimeWidget,
|
|
77
|
+
time: TimeWidget,
|
|
70
78
|
password: PasswordWidget,
|
|
71
79
|
file: FileWidget,
|
|
72
80
|
image: ImageWidget,
|
|
@@ -90,6 +98,10 @@ export const widgetMapping = {
|
|
|
90
98
|
color_picker: ColorPickerWidget,
|
|
91
99
|
select: SelectWidget,
|
|
92
100
|
schema: SchemaWidget,
|
|
101
|
+
static_text: StaticTextWidget,
|
|
102
|
+
hidden: HiddenWidget,
|
|
103
|
+
radio_group: RadioGroupWidget,
|
|
104
|
+
checkbox_group: CheckboxGroupWidget,
|
|
93
105
|
},
|
|
94
106
|
vocabulary: {
|
|
95
107
|
'plone.app.vocabularies.Catalog': ObjectBrowserWidget,
|
|
@@ -139,6 +151,8 @@ export const widgetMapping = {
|
|
|
139
151
|
title: TitleViewWidget,
|
|
140
152
|
url: UrlViewWidget,
|
|
141
153
|
internal_url: InternalUrlWidget,
|
|
154
|
+
static_text: StaticTextViewWidget,
|
|
155
|
+
hidden: HiddenViewWidget,
|
|
142
156
|
object: () => '', // TODO: Not implemented yet: Object View widget
|
|
143
157
|
},
|
|
144
158
|
vocabulary: {},
|