@plone/volto 18.10.0 → 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 (111) hide show
  1. package/CHANGELOG.md +59 -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/ObjectBrowserWidget.jsx +1 -1
  56. package/src/components/manage/Widgets/RadioGroupWidget.jsx +207 -0
  57. package/src/components/manage/Widgets/RadioGroupWidget.stories.jsx +39 -0
  58. package/src/components/manage/Widgets/RadioGroupWidget.test.jsx +46 -0
  59. package/src/components/manage/Widgets/SchemaWidget.jsx +806 -320
  60. package/src/components/manage/Widgets/SelectWidget.jsx +38 -2
  61. package/src/components/manage/Widgets/StaticTextWidget.jsx +34 -0
  62. package/src/components/manage/Widgets/StaticTextWidget.stories.jsx +20 -0
  63. package/src/components/manage/Widgets/StaticTextWidget.test.jsx +25 -0
  64. package/src/components/manage/Widgets/TimeWidget.jsx +96 -0
  65. package/src/components/manage/Widgets/TimeWidget.stories.jsx +22 -0
  66. package/src/components/manage/Widgets/TimeWidget.test.jsx +35 -0
  67. package/src/components/manage/Widgets/index.tsx +35 -0
  68. package/src/components/theme/ContactForm/ContactForm.jsx +7 -4
  69. package/src/components/theme/Widgets/HiddenWidget.jsx +2 -0
  70. package/src/components/theme/Widgets/HiddenWidget.stories.jsx +25 -0
  71. package/src/components/theme/Widgets/HiddenWidget.test.jsx +11 -0
  72. package/src/components/theme/Widgets/StaticTextWidget.jsx +16 -0
  73. package/src/components/theme/Widgets/StaticTextWidget.stories.jsx +29 -0
  74. package/src/components/theme/Widgets/StaticTextWidget.test.jsx +32 -0
  75. package/src/config/Blocks.jsx +2 -0
  76. package/src/config/Widgets.jsx +14 -0
  77. package/src/constants/Languages.cjs +1 -0
  78. package/src/helpers/FormValidation/FormValidation.jsx +4 -0
  79. package/src/helpers/FormValidation/FormValidation.test.js +147 -31
  80. package/src/helpers/FormValidation/validators.ts +4 -1
  81. package/src/helpers/Utils/Utils.jsx +14 -2
  82. package/theme/themes/pastanaga/collections/form.overrides +4 -0
  83. package/theme/themes/pastanaga/elements/input.overrides +7 -0
  84. package/theme/themes/pastanaga/extras/sidebar.less +2 -0
  85. package/types/components/index.d.ts +1 -1
  86. package/types/components/manage/Widgets/CheckboxGroupWidget.d.ts +6 -0
  87. package/types/components/manage/Widgets/CheckboxGroupWidget.stories.d.ts +15 -0
  88. package/types/components/manage/Widgets/CheckboxGroupWidget.test.d.ts +1 -0
  89. package/types/components/manage/Widgets/HiddenWidget.d.ts +54 -0
  90. package/types/components/manage/Widgets/HiddenWidget.stories.d.ts +9 -0
  91. package/types/components/manage/Widgets/HiddenWidget.test.d.ts +1 -0
  92. package/types/components/manage/Widgets/RadioGroupWidget.d.ts +6 -0
  93. package/types/components/manage/Widgets/RadioGroupWidget.stories.d.ts +15 -0
  94. package/types/components/manage/Widgets/RadioGroupWidget.test.d.ts +1 -0
  95. package/types/components/manage/Widgets/StaticTextWidget.d.ts +18 -0
  96. package/types/components/manage/Widgets/StaticTextWidget.stories.d.ts +9 -0
  97. package/types/components/manage/Widgets/StaticTextWidget.test.d.ts +1 -0
  98. package/types/components/manage/Widgets/TimeWidget.d.ts +2 -0
  99. package/types/components/manage/Widgets/TimeWidget.stories.d.ts +8 -0
  100. package/types/components/manage/Widgets/TimeWidget.test.d.ts +1 -0
  101. package/types/components/manage/Widgets/index.d.ts +5 -0
  102. package/types/components/theme/Widgets/HiddenWidget.d.ts +6 -0
  103. package/types/components/theme/Widgets/HiddenWidget.stories.d.ts +8 -0
  104. package/types/components/theme/Widgets/HiddenWidget.test.d.ts +1 -0
  105. package/types/components/theme/Widgets/StaticTextWidget.d.ts +5 -0
  106. package/types/components/theme/Widgets/StaticTextWidget.stories.d.ts +8 -0
  107. package/types/components/theme/Widgets/StaticTextWidget.test.d.ts +1 -0
  108. package/types/config/Widgets.d.ts +14 -0
  109. package/types/constants/Languages.d.cts +1 -0
  110. package/types/helpers/FormValidation/validators.d.ts +1 -1
  111. package/types/helpers/Utils/Utils.d.ts +1 -1
@@ -0,0 +1,207 @@
1
+ /**
2
+ * RadioGroupWidget component.
3
+ * @module components/manage/Widgets/RadioGroupWidget
4
+ */
5
+
6
+ import React, { Component } from 'react';
7
+ import PropTypes from 'prop-types';
8
+ import { connect } from 'react-redux';
9
+ import filter from 'lodash/filter';
10
+ import map from 'lodash/map';
11
+ import { injectIntl } from 'react-intl';
12
+ import {
13
+ getVocabFromHint,
14
+ getVocabFromField,
15
+ getVocabFromItems,
16
+ } from '@plone/volto/helpers/Vocabularies/Vocabularies.js';
17
+ import { Radio } from 'semantic-ui-react';
18
+ import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
19
+ import {
20
+ getVocabulary,
21
+ getVocabularyTokenTitle,
22
+ } from '@plone/volto/actions/vocabularies/vocabularies';
23
+
24
+ /**
25
+ * RadioGroupWidget component class.
26
+ * @function RadioGroupWidget
27
+ * @returns {string} Markup of the component.
28
+ */
29
+ class RadioGroupWidget extends Component {
30
+ /**
31
+ * Property types.
32
+ * @property {Object} propTypes Property types.
33
+ * @static
34
+ */
35
+ static propTypes = {
36
+ id: PropTypes.string.isRequired,
37
+ title: PropTypes.string.isRequired,
38
+ description: PropTypes.string,
39
+ required: PropTypes.bool,
40
+ filterChoices: PropTypes.arrayOf(PropTypes.string),
41
+ error: PropTypes.arrayOf(PropTypes.string),
42
+ getVocabulary: PropTypes.func.isRequired,
43
+ getVocabularyTokenTitle: PropTypes.func.isRequired,
44
+ choices: PropTypes.arrayOf(
45
+ PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
46
+ ),
47
+ items: PropTypes.shape({
48
+ vocabulary: PropTypes.object,
49
+ }),
50
+ widgetOptions: PropTypes.shape({
51
+ vocabulary: PropTypes.object,
52
+ }),
53
+ value: PropTypes.oneOfType([
54
+ PropTypes.object,
55
+ PropTypes.string,
56
+ PropTypes.bool,
57
+ PropTypes.func,
58
+ PropTypes.array,
59
+ ]),
60
+ onChange: PropTypes.func.isRequired,
61
+ onBlur: PropTypes.func,
62
+ onClick: PropTypes.func,
63
+ onEdit: PropTypes.func,
64
+ onDelete: PropTypes.func,
65
+ };
66
+
67
+ /**
68
+ * Default properties
69
+ * @property {Object} defaultProps Default properties.
70
+ * @static
71
+ */
72
+ static defaultProps = {
73
+ description: null,
74
+ required: false,
75
+ filterChoices: null,
76
+ items: {
77
+ vocabulary: null,
78
+ },
79
+ widgetOptions: {
80
+ vocabulary: null,
81
+ },
82
+ error: [],
83
+ choices: [],
84
+ value: null,
85
+ onChange: () => {},
86
+ onBlur: () => {},
87
+ onClick: () => {},
88
+ onEdit: null,
89
+ onDelete: null,
90
+ };
91
+
92
+ /**
93
+ * Component did mount
94
+ * @method componentDidMount
95
+ * @returns {undefined}
96
+ */
97
+ componentDidMount() {
98
+ if (
99
+ (!this.props.choices || this.props.choices?.length === 0) &&
100
+ this.props.vocabBaseUrl
101
+ ) {
102
+ this.props.getVocabulary({
103
+ vocabNameOrURL: this.props.vocabBaseUrl,
104
+ size: -1,
105
+ subrequest: this.props.lang,
106
+ });
107
+ }
108
+ }
109
+
110
+ componentDidUpdate(prevProps) {
111
+ if (
112
+ this.props.vocabBaseUrl !== prevProps.vocabBaseUrl &&
113
+ (!this.props.choices || this.props.choices?.length === 0)
114
+ ) {
115
+ this.props.getVocabulary({
116
+ vocabNameOrURL: this.props.vocabBaseUrl,
117
+ size: -1,
118
+ subrequest: this.props.lang,
119
+ });
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Render method.
125
+ * @method render
126
+ * @returns {string} Markup for the component.
127
+ */
128
+ render() {
129
+ const { id, choices, value, onChange, filterChoices, additionalChoices } =
130
+ this.props;
131
+
132
+ let options = this.props.vocabBaseUrl
133
+ ? this.props.choices
134
+ : [
135
+ ...map(choices, (option) => ({
136
+ value: option[0],
137
+ label:
138
+ // Fix "None" on the serializer, to remove when fixed in p.restapi
139
+ option[1] !== 'None' && option[1] ? option[1] : option[0],
140
+ })),
141
+ ];
142
+
143
+ if (additionalChoices) {
144
+ options = [...(options || []), ...additionalChoices];
145
+ }
146
+
147
+ if (filterChoices) {
148
+ options = filter(options, (item) => filterChoices.includes(item.value));
149
+ }
150
+
151
+ return (
152
+ <FormFieldWrapper {...this.props}>
153
+ {options.map((option) => (
154
+ <Radio
155
+ label={option.label}
156
+ name={id}
157
+ key={option.value}
158
+ value={option.value}
159
+ checked={option.value === value}
160
+ onChange={(e, { value }) => {
161
+ onChange(id, value);
162
+ }}
163
+ />
164
+ ))}
165
+ </FormFieldWrapper>
166
+ );
167
+ }
168
+ }
169
+
170
+ export const RadioGroupWidgetComponent = injectIntl(RadioGroupWidget);
171
+
172
+ export default connect(
173
+ (state, props) => {
174
+ const vocabBaseUrl = !props.choices
175
+ ? getVocabFromHint(props) ||
176
+ getVocabFromField(props) ||
177
+ getVocabFromItems(props)
178
+ : '';
179
+
180
+ const vocabState =
181
+ state.vocabularies?.[vocabBaseUrl]?.subrequests?.[state.intl.locale];
182
+
183
+ // If the schema already has the choices in it, then do not try to get the vocab,
184
+ // even if there is one
185
+ if (props.choices) {
186
+ return {
187
+ choices: props.choices,
188
+ lang: state.intl.locale,
189
+ };
190
+ } else if (vocabState) {
191
+ return {
192
+ vocabBaseUrl,
193
+ choices: vocabState?.items ?? [],
194
+ lang: state.intl.locale,
195
+ };
196
+ // There is a moment that vocabState is not there yet, so we need to pass the
197
+ // vocabBaseUrl to the component.
198
+ } else if (vocabBaseUrl) {
199
+ return {
200
+ vocabBaseUrl,
201
+ lang: state.intl.locale,
202
+ };
203
+ }
204
+ return { lang: state.intl.locale };
205
+ },
206
+ { getVocabulary, getVocabularyTokenTitle },
207
+ )(RadioGroupWidgetComponent);
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import RadioGroupWidget, {
3
+ RadioGroupWidgetComponent,
4
+ } from './RadioGroupWidget';
5
+ import WidgetStory from './story';
6
+
7
+ export const Default = WidgetStory.bind({
8
+ widget: RadioGroupWidget,
9
+ });
10
+ Default.args = {
11
+ id: 'field-empty',
12
+ title: 'field 1 title',
13
+ description: 'Optional help text',
14
+ choices: [
15
+ ['Foo', 'Foo'],
16
+ ['Bar', 'Bar'],
17
+ ['FooBar', 'FooBar'],
18
+ ],
19
+ };
20
+
21
+ export default {
22
+ title: 'Edit Widgets/Radio Group Widget',
23
+ component: RadioGroupWidgetComponent,
24
+ decorators: [
25
+ (Story) => (
26
+ <div style={{ width: '400px' }}>
27
+ <Story />
28
+ </div>
29
+ ),
30
+ ],
31
+ argTypes: {
32
+ // controlled value prop
33
+ value: {
34
+ control: {
35
+ disable: true,
36
+ },
37
+ },
38
+ },
39
+ };
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import configureStore from 'redux-mock-store';
3
+ import { Provider } from 'react-intl-redux';
4
+ import { waitFor, render, screen } from '@testing-library/react';
5
+
6
+ import RadioGroupWidget from './RadioGroupWidget';
7
+
8
+ const mockStore = configureStore();
9
+
10
+ jest.mock('@plone/volto/helpers/Loadable/Loadable');
11
+ beforeAll(
12
+ async () =>
13
+ await require('@plone/volto/helpers/Loadable/Loadable').__setLoadables(),
14
+ );
15
+
16
+ test('renders a radio group widget component', async () => {
17
+ const store = mockStore({
18
+ intl: {
19
+ locale: 'en',
20
+ messages: {},
21
+ },
22
+ vocabularies: {
23
+ 'plone.app.vocabularies.Keywords': {
24
+ items: [{ title: 'My item', value: 'myitem' }],
25
+ itemsTotal: 1,
26
+ },
27
+ },
28
+ });
29
+
30
+ const { container } = render(
31
+ <Provider store={store}>
32
+ <RadioGroupWidget
33
+ id="my-field"
34
+ title="My field"
35
+ fieldSet="default"
36
+ onChange={() => {}}
37
+ onBlur={() => {}}
38
+ onClick={() => {}}
39
+ items={{ vocabulary: { '@id': 'plone.app.vocabularies.Keywords' } }}
40
+ />
41
+ </Provider>,
42
+ );
43
+
44
+ await waitFor(() => screen.getByText('My field'));
45
+ expect(container).toMatchSnapshot();
46
+ });