@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.
Files changed (110) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +3 -3
  3. package/locales/ca/LC_MESSAGES/volto.po +46 -0
  4. package/locales/ca.json +1 -1
  5. package/locales/de/LC_MESSAGES/volto.po +46 -0
  6. package/locales/de.json +1 -1
  7. package/locales/en/LC_MESSAGES/volto.po +46 -0
  8. package/locales/en.json +1 -1
  9. package/locales/es/LC_MESSAGES/volto.po +46 -0
  10. package/locales/es.json +1 -1
  11. package/locales/eu/LC_MESSAGES/volto.po +46 -0
  12. package/locales/eu.json +1 -1
  13. package/locales/fi/LC_MESSAGES/volto.po +46 -0
  14. package/locales/fi.json +1 -1
  15. package/locales/fr/LC_MESSAGES/volto.po +46 -0
  16. package/locales/fr.json +1 -1
  17. package/locales/hi/LC_MESSAGES/volto.po +46 -0
  18. package/locales/hi.json +1 -1
  19. package/locales/it/LC_MESSAGES/volto.po +46 -0
  20. package/locales/it.json +1 -1
  21. package/locales/ja/LC_MESSAGES/volto.po +46 -0
  22. package/locales/ja.json +1 -1
  23. package/locales/nl/LC_MESSAGES/volto.po +46 -0
  24. package/locales/nl.json +1 -1
  25. package/locales/pt/LC_MESSAGES/volto.po +46 -0
  26. package/locales/pt.json +1 -1
  27. package/locales/pt_BR/LC_MESSAGES/volto.po +47 -1
  28. package/locales/pt_BR.json +1 -1
  29. package/locales/ro/LC_MESSAGES/volto.po +46 -0
  30. package/locales/ro.json +1 -1
  31. package/locales/ru/LC_MESSAGES/volto.po +5243 -0
  32. package/locales/ru.json +1 -0
  33. package/locales/volto.pot +47 -1
  34. package/locales/zh_CN/LC_MESSAGES/volto.po +46 -0
  35. package/locales/zh_CN.json +1 -1
  36. package/package.json +4 -4
  37. package/razzle.config.js +1 -1
  38. package/src/components/index.js +2 -0
  39. package/src/components/manage/Blocks/Block/BlocksForm.jsx +5 -1
  40. package/src/components/manage/Blocks/Search/components/SearchInput.jsx +11 -1
  41. package/src/components/manage/DragDropList/DragDropList.jsx +78 -33
  42. package/src/components/manage/Form/Field.jsx +38 -29
  43. package/src/components/manage/Form/Form.jsx +141 -69
  44. package/src/components/manage/Form/ModalForm.jsx +29 -0
  45. package/src/components/manage/Sidebar/ObjectBrowserNav.jsx +1 -1
  46. package/src/components/manage/Sidebar/ObjectBrowserNav.test.jsx +34 -13
  47. package/src/components/manage/Widgets/CheckboxGroupWidget.jsx +214 -0
  48. package/src/components/manage/Widgets/CheckboxGroupWidget.stories.jsx +39 -0
  49. package/src/components/manage/Widgets/CheckboxGroupWidget.test.jsx +45 -0
  50. package/src/components/manage/Widgets/FileWidget.jsx +43 -2
  51. package/src/components/manage/Widgets/HiddenWidget.jsx +114 -0
  52. package/src/components/manage/Widgets/HiddenWidget.stories.jsx +20 -0
  53. package/src/components/manage/Widgets/HiddenWidget.test.jsx +32 -0
  54. package/src/components/manage/Widgets/ImageWidget.jsx +3 -0
  55. package/src/components/manage/Widgets/RadioGroupWidget.jsx +207 -0
  56. package/src/components/manage/Widgets/RadioGroupWidget.stories.jsx +39 -0
  57. package/src/components/manage/Widgets/RadioGroupWidget.test.jsx +46 -0
  58. package/src/components/manage/Widgets/SchemaWidget.jsx +806 -320
  59. package/src/components/manage/Widgets/SelectWidget.jsx +38 -2
  60. package/src/components/manage/Widgets/StaticTextWidget.jsx +34 -0
  61. package/src/components/manage/Widgets/StaticTextWidget.stories.jsx +20 -0
  62. package/src/components/manage/Widgets/StaticTextWidget.test.jsx +25 -0
  63. package/src/components/manage/Widgets/TimeWidget.jsx +96 -0
  64. package/src/components/manage/Widgets/TimeWidget.stories.jsx +22 -0
  65. package/src/components/manage/Widgets/TimeWidget.test.jsx +35 -0
  66. package/src/components/manage/Widgets/index.tsx +35 -0
  67. package/src/components/theme/ContactForm/ContactForm.jsx +7 -4
  68. package/src/components/theme/Widgets/HiddenWidget.jsx +2 -0
  69. package/src/components/theme/Widgets/HiddenWidget.stories.jsx +25 -0
  70. package/src/components/theme/Widgets/HiddenWidget.test.jsx +11 -0
  71. package/src/components/theme/Widgets/StaticTextWidget.jsx +16 -0
  72. package/src/components/theme/Widgets/StaticTextWidget.stories.jsx +29 -0
  73. package/src/components/theme/Widgets/StaticTextWidget.test.jsx +32 -0
  74. package/src/config/Blocks.jsx +2 -0
  75. package/src/config/Widgets.jsx +14 -0
  76. package/src/constants/Languages.cjs +1 -0
  77. package/src/helpers/FormValidation/FormValidation.jsx +4 -0
  78. package/src/helpers/FormValidation/FormValidation.test.js +147 -31
  79. package/src/helpers/FormValidation/validators.ts +4 -1
  80. package/src/helpers/Utils/Utils.jsx +14 -2
  81. package/theme/themes/pastanaga/collections/form.overrides +4 -0
  82. package/theme/themes/pastanaga/elements/input.overrides +7 -0
  83. package/theme/themes/pastanaga/extras/sidebar.less +2 -0
  84. package/types/components/index.d.ts +1 -1
  85. package/types/components/manage/Widgets/CheckboxGroupWidget.d.ts +6 -0
  86. package/types/components/manage/Widgets/CheckboxGroupWidget.stories.d.ts +15 -0
  87. package/types/components/manage/Widgets/CheckboxGroupWidget.test.d.ts +1 -0
  88. package/types/components/manage/Widgets/HiddenWidget.d.ts +54 -0
  89. package/types/components/manage/Widgets/HiddenWidget.stories.d.ts +9 -0
  90. package/types/components/manage/Widgets/HiddenWidget.test.d.ts +1 -0
  91. package/types/components/manage/Widgets/RadioGroupWidget.d.ts +6 -0
  92. package/types/components/manage/Widgets/RadioGroupWidget.stories.d.ts +15 -0
  93. package/types/components/manage/Widgets/RadioGroupWidget.test.d.ts +1 -0
  94. package/types/components/manage/Widgets/StaticTextWidget.d.ts +18 -0
  95. package/types/components/manage/Widgets/StaticTextWidget.stories.d.ts +9 -0
  96. package/types/components/manage/Widgets/StaticTextWidget.test.d.ts +1 -0
  97. package/types/components/manage/Widgets/TimeWidget.d.ts +2 -0
  98. package/types/components/manage/Widgets/TimeWidget.stories.d.ts +8 -0
  99. package/types/components/manage/Widgets/TimeWidget.test.d.ts +1 -0
  100. package/types/components/manage/Widgets/index.d.ts +5 -0
  101. package/types/components/theme/Widgets/HiddenWidget.d.ts +6 -0
  102. package/types/components/theme/Widgets/HiddenWidget.stories.d.ts +8 -0
  103. package/types/components/theme/Widgets/HiddenWidget.test.d.ts +1 -0
  104. package/types/components/theme/Widgets/StaticTextWidget.d.ts +5 -0
  105. package/types/components/theme/Widgets/StaticTextWidget.stories.d.ts +8 -0
  106. package/types/components/theme/Widgets/StaticTextWidget.test.d.ts +1 -0
  107. package/types/config/Widgets.d.ts +14 -0
  108. package/types/constants/Languages.d.cts +1 -0
  109. package/types/helpers/FormValidation/validators.d.ts +1 -1
  110. 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 { id, choices, value, intl, onChange } = this.props;
193
- const normalizedValue = normalizeValue(choices, value, intl);
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, Icon } from 'semantic-ui-react';
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="arrow left"
165
- size="big"
166
- color="blue"
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,2 @@
1
+ const HiddenWidget = ({ value, children, className }) => '';
2
+ export default HiddenWidget;
@@ -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
+ });
@@ -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,
@@ -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: {},
@@ -15,6 +15,7 @@ module.exports = {
15
15
  it: 'Italiano',
16
16
  nl: 'Nederlands',
17
17
  ro: 'Română',
18
+ ru: 'Русский',
18
19
  ja: '日本語',
19
20
  pt: 'Português',
20
21
  pt_BR: 'Português (Brasil)',
@@ -233,6 +233,10 @@ const validateFieldsPerFieldset = (
233
233
  ...blockTypeFieldErrors,
234
234
  ];
235
235
  }
236
+
237
+ if (errors[fieldId]) {
238
+ errors[fieldId].title = field.title;
239
+ }
236
240
  });
237
241
 
238
242
  return errors;