@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
@@ -0,0 +1,214 @@
1
+ /**
2
+ * CheckboxGroupWidget component.
3
+ * @module components/manage/Widgets/CheckboxGroupWidget
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 includes from 'lodash/includes';
11
+ import map from 'lodash/map';
12
+ import without from 'lodash/without';
13
+ import { injectIntl } from 'react-intl';
14
+ import {
15
+ getVocabFromHint,
16
+ getVocabFromField,
17
+ getVocabFromItems,
18
+ } from '@plone/volto/helpers/Vocabularies/Vocabularies';
19
+ import { Checkbox } from 'semantic-ui-react';
20
+ import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
21
+ import {
22
+ getVocabulary,
23
+ getVocabularyTokenTitle,
24
+ } from '@plone/volto/actions/vocabularies/vocabularies';
25
+
26
+ /**
27
+ * CheckboxGroupWidget component class.
28
+ * @function CheckboxGroupWidget
29
+ * @returns {string} Markup of the component.
30
+ */
31
+ class CheckboxGroupWidget extends Component {
32
+ /**
33
+ * Property types.
34
+ * @property {Object} propTypes Property types.
35
+ * @static
36
+ */
37
+ static propTypes = {
38
+ id: PropTypes.string.isRequired,
39
+ title: PropTypes.string.isRequired,
40
+ description: PropTypes.string,
41
+ required: PropTypes.bool,
42
+ filterChoices: PropTypes.arrayOf(PropTypes.string),
43
+ error: PropTypes.arrayOf(PropTypes.string),
44
+ getVocabulary: PropTypes.func.isRequired,
45
+ getVocabularyTokenTitle: PropTypes.func.isRequired,
46
+ choices: PropTypes.arrayOf(
47
+ PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
48
+ ),
49
+ items: PropTypes.shape({
50
+ vocabulary: PropTypes.object,
51
+ }),
52
+ widgetOptions: PropTypes.shape({
53
+ vocabulary: PropTypes.object,
54
+ }),
55
+ value: PropTypes.oneOfType([
56
+ PropTypes.object,
57
+ PropTypes.string,
58
+ PropTypes.bool,
59
+ PropTypes.func,
60
+ PropTypes.array,
61
+ ]),
62
+ onChange: PropTypes.func.isRequired,
63
+ onBlur: PropTypes.func,
64
+ onClick: PropTypes.func,
65
+ onEdit: PropTypes.func,
66
+ onDelete: PropTypes.func,
67
+ };
68
+
69
+ /**
70
+ * Default properties
71
+ * @property {Object} defaultProps Default properties.
72
+ * @static
73
+ */
74
+ static defaultProps = {
75
+ description: null,
76
+ required: false,
77
+ filterChoices: null,
78
+ items: {
79
+ vocabulary: null,
80
+ },
81
+ widgetOptions: {
82
+ vocabulary: null,
83
+ },
84
+ error: [],
85
+ choices: [],
86
+ value: null,
87
+ onChange: () => {},
88
+ onBlur: () => {},
89
+ onClick: () => {},
90
+ onEdit: null,
91
+ onDelete: null,
92
+ };
93
+
94
+ /**
95
+ * Component did mount
96
+ * @method componentDidMount
97
+ * @returns {undefined}
98
+ */
99
+ componentDidMount() {
100
+ if (
101
+ (!this.props.choices || this.props.choices?.length === 0) &&
102
+ this.props.vocabBaseUrl
103
+ ) {
104
+ this.props.getVocabulary({
105
+ vocabNameOrURL: this.props.vocabBaseUrl,
106
+ size: -1,
107
+ subrequest: this.props.lang,
108
+ });
109
+ }
110
+ }
111
+
112
+ componentDidUpdate(prevProps) {
113
+ if (
114
+ this.props.vocabBaseUrl !== prevProps.vocabBaseUrl &&
115
+ (!this.props.choices || this.props.choices?.length === 0)
116
+ ) {
117
+ this.props.getVocabulary({
118
+ vocabNameOrURL: this.props.vocabBaseUrl,
119
+ size: -1,
120
+ subrequest: this.props.lang,
121
+ });
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Render method.
127
+ * @method render
128
+ * @returns {string} Markup for the component.
129
+ */
130
+ render() {
131
+ const { id, choices, value, onChange, filterChoices, additionalChoices } =
132
+ this.props;
133
+
134
+ let options = this.props.vocabBaseUrl
135
+ ? this.props.choices
136
+ : [
137
+ ...map(choices, (option) => ({
138
+ value: option[0],
139
+ label:
140
+ // Fix "None" on the serializer, to remove when fixed in p.restapi
141
+ option[1] !== 'None' && option[1] ? option[1] : option[0],
142
+ })),
143
+ ];
144
+
145
+ if (additionalChoices) {
146
+ options = [...(options || []), ...additionalChoices];
147
+ }
148
+
149
+ if (filterChoices) {
150
+ options = filter(options, (item) => filterChoices.includes(item.value));
151
+ }
152
+
153
+ return (
154
+ <FormFieldWrapper {...this.props}>
155
+ {options.map((option) => (
156
+ <Checkbox
157
+ label={option.label}
158
+ key={option.value}
159
+ name={id}
160
+ value={option.value}
161
+ checked={includes(value, option.value)}
162
+ onChange={(e, data) => {
163
+ const newValue = value || [];
164
+ if (data.checked) {
165
+ onChange(id, [...newValue, data.value]);
166
+ } else {
167
+ onChange(id, without(newValue, data.value));
168
+ }
169
+ }}
170
+ />
171
+ ))}
172
+ </FormFieldWrapper>
173
+ );
174
+ }
175
+ }
176
+
177
+ export const CheckboxGroupWidgetComponent = injectIntl(CheckboxGroupWidget);
178
+
179
+ export default connect(
180
+ (state, props) => {
181
+ const vocabBaseUrl = !props.choices
182
+ ? getVocabFromHint(props) ||
183
+ getVocabFromField(props) ||
184
+ getVocabFromItems(props)
185
+ : '';
186
+
187
+ const vocabState =
188
+ state.vocabularies?.[vocabBaseUrl]?.subrequests?.[state.intl.locale];
189
+
190
+ // If the schema already has the choices in it, then do not try to get the vocab,
191
+ // even if there is one
192
+ if (props.choices) {
193
+ return {
194
+ choices: props.choices,
195
+ lang: state.intl.locale,
196
+ };
197
+ } else if (vocabState) {
198
+ return {
199
+ vocabBaseUrl,
200
+ choices: vocabState?.items ?? [],
201
+ lang: state.intl.locale,
202
+ };
203
+ // There is a moment that vocabState is not there yet, so we need to pass the
204
+ // vocabBaseUrl to the component.
205
+ } else if (vocabBaseUrl) {
206
+ return {
207
+ vocabBaseUrl,
208
+ lang: state.intl.locale,
209
+ };
210
+ }
211
+ return { lang: state.intl.locale };
212
+ },
213
+ { getVocabulary, getVocabularyTokenTitle },
214
+ )(CheckboxGroupWidgetComponent);
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import CheckboxGroupWidget, {
3
+ CheckboxGroupWidgetComponent,
4
+ } from './CheckboxGroupWidget';
5
+ import WidgetStory from './story';
6
+
7
+ export const Default = WidgetStory.bind({
8
+ widget: CheckboxGroupWidget,
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/Checkbox Group Widget',
23
+ component: CheckboxGroupWidgetComponent,
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,45 @@
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 CheckboxGroupWidget from './CheckboxGroupWidget';
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 checkbox 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
+ <CheckboxGroupWidget
33
+ id="my-field"
34
+ title="My field"
35
+ fieldSet="default"
36
+ onChange={() => {}}
37
+ onBlur={() => {}}
38
+ onClick={() => {}}
39
+ />
40
+ </Provider>,
41
+ );
42
+
43
+ await waitFor(() => screen.getByText('My field'));
44
+ expect(container).toMatchSnapshot();
45
+ });
@@ -10,12 +10,14 @@ import { readAsDataURL } from 'promise-file-reader';
10
10
  import { injectIntl } from 'react-intl';
11
11
  import deleteSVG from '@plone/volto/icons/delete.svg';
12
12
  import Icon from '@plone/volto/components/theme/Icon/Icon';
13
+ import Toast from '@plone/volto/components/manage/Toast/Toast';
13
14
  import UniversalLink from '@plone/volto/components/manage/UniversalLink/UniversalLink';
14
15
  import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
15
16
  import loadable from '@loadable/component';
16
17
  import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
17
18
  import { validateFileUploadSize } from '@plone/volto/helpers/FormValidation/FormValidation';
18
19
  import { defineMessages, useIntl } from 'react-intl';
20
+ import { toast } from 'react-toastify';
19
21
 
20
22
  const imageMimetypes = [
21
23
  'image/png',
@@ -48,6 +50,15 @@ const messages = defineMessages({
48
50
  id: 'Choose a file',
49
51
  defaultMessage: 'Choose a file',
50
52
  },
53
+ maxSizeError: {
54
+ id: 'The file you uploaded exceeded the maximum allowed size of {size} bytes',
55
+ defaultMessage:
56
+ 'The file you uploaded exceeded the maximum allowed size of {size} bytes',
57
+ },
58
+ acceptError: {
59
+ id: 'File is not of the accepted type {accept}',
60
+ defaultMessage: 'File is not of the accepted type {accept}',
61
+ },
51
62
  });
52
63
 
53
64
  /**
@@ -96,7 +107,33 @@ const FileWidget = (props) => {
96
107
  * @param {array} files File objects
97
108
  * @returns {undefined}
98
109
  */
99
- const onDrop = (files) => {
110
+ const onDrop = (files, rejectedFiles) => {
111
+ rejectedFiles.forEach((file) => {
112
+ file.errors.forEach((err) => {
113
+ if (err.code === 'file-too-large') {
114
+ toast.error(
115
+ <Toast
116
+ error
117
+ title={intl.formatMessage(messages.maxSizeError, {
118
+ size: props.size,
119
+ })}
120
+ />,
121
+ );
122
+ }
123
+
124
+ if (err.code === 'file-invalid-type') {
125
+ toast.error(
126
+ <Toast
127
+ error
128
+ title={intl.formatMessage(messages.acceptError, {
129
+ accept: props.accept,
130
+ })}
131
+ />,
132
+ );
133
+ }
134
+ });
135
+ });
136
+ if (files.length < 1) return;
100
137
  const file = files[0];
101
138
  if (!validateFileUploadSize(file, intl.formatMessage)) return;
102
139
  readAsDataURL(file).then((data) => {
@@ -125,7 +162,11 @@ const FileWidget = (props) => {
125
162
 
126
163
  return (
127
164
  <FormFieldWrapper {...props}>
128
- <Dropzone onDrop={onDrop}>
165
+ <Dropzone
166
+ onDrop={onDrop}
167
+ {...(props.size ? { maxSize: props.size } : {})}
168
+ {...(props.accept ? { accept: props.accept } : {})}
169
+ >
129
170
  {({ getRootProps, getInputProps, isDragActive }) => (
130
171
  <div className="file-widget-dropzone" {...getRootProps()}>
131
172
  {isDragActive && <Dimmer active></Dimmer>}
@@ -0,0 +1,114 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Input } from 'semantic-ui-react';
4
+
5
+ import Icon from '@plone/volto/components/theme/Icon/Icon';
6
+ import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
7
+
8
+ const HiddenWidget = (props) => {
9
+ const {
10
+ id,
11
+ value,
12
+ onChange,
13
+ onBlur,
14
+ onClick,
15
+ icon,
16
+ iconAction,
17
+ minLength,
18
+ maxLength,
19
+ placeholder,
20
+ isDisabled,
21
+ focus,
22
+ onEdit,
23
+ } = props;
24
+
25
+ const ref = useRef();
26
+
27
+ useEffect(() => {
28
+ if (focus) {
29
+ ref.current.focus();
30
+ }
31
+ // eslint-disable-next-line react-hooks/exhaustive-deps
32
+ }, []);
33
+
34
+ return onEdit ? (
35
+ <FormFieldWrapper {...props} className="text">
36
+ <Input
37
+ id={`field-${id}`}
38
+ name={id}
39
+ value={value || ''}
40
+ disabled={isDisabled}
41
+ icon={icon || null}
42
+ placeholder={placeholder}
43
+ onChange={({ target }) =>
44
+ onChange(id, target.value === '' ? undefined : target.value)
45
+ }
46
+ ref={ref}
47
+ onBlur={({ target }) =>
48
+ onBlur(id, target.value === '' ? undefined : target.value)
49
+ }
50
+ onClick={() => onClick()}
51
+ minLength={minLength || null}
52
+ maxLength={maxLength || null}
53
+ />
54
+ {icon && iconAction && (
55
+ <button className={`field-${id}-action-button`} onClick={iconAction}>
56
+ <Icon name={icon} size="18px" />
57
+ </button>
58
+ )}
59
+ </FormFieldWrapper>
60
+ ) : (
61
+ <input
62
+ id={`field-${id}`}
63
+ name={id}
64
+ value={value || ''}
65
+ placeholder={placeholder}
66
+ ref={ref}
67
+ type="hidden"
68
+ />
69
+ );
70
+ };
71
+
72
+ export default HiddenWidget;
73
+
74
+ HiddenWidget.propTypes = {
75
+ id: PropTypes.string.isRequired,
76
+ title: PropTypes.string.isRequired,
77
+ description: PropTypes.string,
78
+ required: PropTypes.bool,
79
+ error: PropTypes.arrayOf(PropTypes.string),
80
+ value: PropTypes.string,
81
+ focus: PropTypes.bool,
82
+ onChange: PropTypes.func,
83
+ onBlur: PropTypes.func,
84
+ onClick: PropTypes.func,
85
+ onEdit: PropTypes.func,
86
+ onDelete: PropTypes.func,
87
+ icon: PropTypes.shape({
88
+ xmlns: PropTypes.string,
89
+ viewBox: PropTypes.string,
90
+ content: PropTypes.string,
91
+ }),
92
+ iconAction: PropTypes.func,
93
+ minLength: PropTypes.number,
94
+ maxLength: PropTypes.number,
95
+ wrapped: PropTypes.bool,
96
+ placeholder: PropTypes.string,
97
+ };
98
+
99
+ HiddenWidget.defaultProps = {
100
+ description: null,
101
+ required: false,
102
+ error: [],
103
+ value: null,
104
+ onChange: () => {},
105
+ onBlur: () => {},
106
+ onClick: () => {},
107
+ onEdit: null,
108
+ onDelete: null,
109
+ focus: false,
110
+ icon: null,
111
+ iconAction: null,
112
+ minLength: null,
113
+ maxLength: null,
114
+ };
@@ -0,0 +1,20 @@
1
+ import HiddenWidget from './HiddenWidget';
2
+ import WidgetStory from './story';
3
+
4
+ export const Hidden = WidgetStory.bind({
5
+ props: { id: 'text', title: 'Text' },
6
+ widget: HiddenWidget,
7
+ });
8
+
9
+ export default {
10
+ title: 'Edit Widgets/Hidden',
11
+ component: HiddenWidget,
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,32 @@
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 HiddenWidget from './HiddenWidget';
7
+
8
+ const mockStore = configureStore();
9
+
10
+ test('renders a hidden 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
+ <HiddenWidget
21
+ id="my-field"
22
+ title="My field"
23
+ fieldSet="default"
24
+ onChange={() => {}}
25
+ onBlur={() => {}}
26
+ onClick={() => {}}
27
+ />
28
+ </Provider>,
29
+ );
30
+ const json = component.toJSON();
31
+ expect(json).toMatchSnapshot();
32
+ });
@@ -227,6 +227,7 @@ const UnconnectedImageInput = (props) => {
227
227
  currentPath: contextUrl,
228
228
  });
229
229
  }}
230
+ type="button"
230
231
  >
231
232
  <Icon name={navTreeSVG} size="24px" />
232
233
  </Button>
@@ -242,6 +243,7 @@ const UnconnectedImageInput = (props) => {
242
243
  onClick={() => {
243
244
  imageUploadInputRef.current.click();
244
245
  }}
246
+ type="button"
245
247
  >
246
248
  <Icon name={uploadSVG} size="24px" />
247
249
  </Button>
@@ -266,6 +268,7 @@ const UnconnectedImageInput = (props) => {
266
268
  !props.selected && onFocus && onFocus();
267
269
  linkEditor.show();
268
270
  }}
271
+ type="button"
269
272
  >
270
273
  <Icon name={linkSVG} circled size="24px" />
271
274
  </Button>