@plone/volto 17.0.0-alpha.24 → 17.0.0-alpha.26

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 (130) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/CHANGELOG.md +92 -4
  3. package/CONTRIBUTING.md +5 -1
  4. package/README.md +9 -7
  5. package/cypress/support/commands.js +12 -9
  6. package/cypress.config.js +1 -0
  7. package/locales/ca/LC_MESSAGES/volto.po +41 -15
  8. package/locales/ca.json +1 -1
  9. package/locales/de/LC_MESSAGES/volto.po +41 -15
  10. package/locales/de.json +1 -1
  11. package/locales/en/LC_MESSAGES/volto.po +40 -14
  12. package/locales/en.json +1 -1
  13. package/locales/es/LC_MESSAGES/volto.po +69 -43
  14. package/locales/es.json +1 -1
  15. package/locales/eu/LC_MESSAGES/volto.po +40 -14
  16. package/locales/eu.json +1 -1
  17. package/locales/fi/LC_MESSAGES/volto.po +40 -14
  18. package/locales/fi.json +1 -1
  19. package/locales/fr/LC_MESSAGES/volto.po +41 -15
  20. package/locales/fr.json +1 -1
  21. package/locales/it/LC_MESSAGES/volto.po +40 -14
  22. package/locales/it.json +1 -1
  23. package/locales/ja/LC_MESSAGES/volto.po +40 -14
  24. package/locales/ja.json +1 -1
  25. package/locales/nl/LC_MESSAGES/volto.po +41 -15
  26. package/locales/nl.json +1 -1
  27. package/locales/pt/LC_MESSAGES/volto.po +41 -15
  28. package/locales/pt.json +1 -1
  29. package/locales/pt_BR/LC_MESSAGES/volto.po +40 -14
  30. package/locales/pt_BR.json +1 -1
  31. package/locales/ro/LC_MESSAGES/volto.po +41 -15
  32. package/locales/ro.json +1 -1
  33. package/locales/volto.pot +41 -15
  34. package/locales/zh_CN/LC_MESSAGES/volto.po +41 -15
  35. package/locales/zh_CN.json +1 -1
  36. package/package.json +4 -4
  37. package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +90 -0
  38. package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +6 -0
  39. package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +6 -0
  40. package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +6 -0
  41. package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +10 -0
  42. package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +10 -0
  43. package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +30 -0
  44. package/packages/volto-slate/build/messages/src/elementEditor/messages.json +10 -0
  45. package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +6 -0
  46. package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +6 -0
  47. package/packages/volto-slate/package.json +1 -1
  48. package/packages/volto-slate/src/editor/render.jsx +2 -3
  49. package/src/actions/index.js +4 -0
  50. package/src/actions/navroot/navroot.js +16 -0
  51. package/src/actions/navroot/navroot.test.js +15 -0
  52. package/src/actions/relations/relations.js +17 -0
  53. package/src/actions/site/site.js +16 -0
  54. package/src/actions/site/site.test.js +15 -0
  55. package/src/actions/userSession/userSession.js +17 -1
  56. package/src/components/manage/Blocks/Block/Settings.jsx +2 -0
  57. package/src/components/manage/Blocks/Block/Settings.test.jsx +90 -0
  58. package/src/components/manage/Blocks/Image/schema.js +5 -1
  59. package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +18 -11
  60. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +42 -25
  61. package/src/components/manage/Blocks/ToC/View.jsx +75 -13
  62. package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.jsx +4 -13
  63. package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.test.jsx +44 -0
  64. package/src/components/manage/Contents/Contents.jsx +27 -0
  65. package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.jsx +65 -38
  66. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +11 -9
  67. package/src/components/manage/Controlpanels/Relations/Relations.jsx +3 -3
  68. package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +8 -7
  69. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +15 -9
  70. package/src/components/manage/Controlpanels/Rules/AddRule.jsx +1 -1
  71. package/src/components/manage/Controlpanels/Rules/EditRule.jsx +1 -1
  72. package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +95 -5
  73. package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +127 -99
  74. package/src/components/manage/Diff/DiffField.jsx +25 -1
  75. package/src/components/manage/Form/BlockDataForm.jsx +3 -2
  76. package/src/components/manage/Form/BlockDataForm.test.jsx +34 -2
  77. package/src/components/manage/LinksToItem/LinksToItem.jsx +1 -1
  78. package/src/components/manage/LinksToItem/LinksToItem.test.jsx +5 -2
  79. package/src/components/manage/Messages/Messages.jsx +32 -99
  80. package/src/components/manage/Messages/Messages.test.jsx +0 -1
  81. package/src/components/manage/Sharing/Sharing.jsx +50 -21
  82. package/src/components/manage/UniversalLink/UniversalLink.jsx +4 -6
  83. package/src/components/manage/Widgets/ArrayWidget.jsx +3 -1
  84. package/src/components/manage/Widgets/ArrayWidget.test.jsx +45 -1
  85. package/src/components/manage/Widgets/FormFieldWrapper.jsx +1 -1
  86. package/src/components/manage/Widgets/RegistryImageWidget.jsx +210 -0
  87. package/src/components/manage/Widgets/RegistryImageWidget.test.jsx +91 -0
  88. package/src/components/manage/Widgets/SelectWidget.jsx +15 -1
  89. package/src/components/manage/Widgets/SelectWidget.test.jsx +45 -1
  90. package/src/components/theme/Comments/Comment.stories.jsx +84 -0
  91. package/src/components/theme/Comments/Comments.jsx +273 -378
  92. package/src/components/theme/ContentMetadataTags/ContentMetadataTags.jsx +37 -3
  93. package/src/components/theme/Login/Login.jsx +159 -241
  94. package/src/components/theme/Logo/Logo.Multilingual.test.jsx +131 -1
  95. package/src/components/theme/Logo/Logo.jsx +35 -29
  96. package/src/components/theme/Logo/Logo.test.jsx +135 -1
  97. package/src/components/theme/Logout/Logout.jsx +36 -83
  98. package/src/components/theme/Navigation/Navigation.jsx +86 -171
  99. package/src/components/theme/Search/SearchTags.jsx +30 -60
  100. package/src/components/theme/SearchWidget/SearchWidget.jsx +15 -3
  101. package/src/components/theme/SearchWidget/SearchWidget.test.jsx +8 -0
  102. package/src/components/theme/Sitemap/Sitemap.jsx +24 -13
  103. package/src/components/theme/Sitemap/Sitemap.test.jsx +23 -2
  104. package/src/components/theme/View/View.jsx +2 -0
  105. package/src/config/ControlPanels.js +0 -1
  106. package/src/config/Widgets.jsx +2 -0
  107. package/src/config/index.js +15 -3
  108. package/src/constants/ActionTypes.js +4 -0
  109. package/src/express-middleware/images.js +1 -0
  110. package/src/helpers/MessageLabels/MessageLabels.js +26 -4
  111. package/src/helpers/Site/index.js +21 -0
  112. package/src/helpers/index.js +1 -0
  113. package/src/reducers/index.js +4 -0
  114. package/src/reducers/navroot/navroot.js +79 -0
  115. package/src/reducers/navroot/navroot.test.js +110 -0
  116. package/src/reducers/relations/relations.js +74 -46
  117. package/src/reducers/site/site.js +51 -0
  118. package/src/reducers/site/site.test.js +67 -0
  119. package/src/reducers/userSession/userSession.js +15 -1
  120. package/src/server.jsx +9 -0
  121. package/test-setup-config.js +1 -0
  122. package/theme/themes/pastanaga/collections/form.overrides +46 -0
  123. package/theme/themes/pastanaga/elements/input.overrides +10 -0
  124. package/theme/themes/pastanaga/elements/label.overrides +10 -0
  125. package/theme/themes/pastanaga/extras/login.less +3 -0
  126. package/webpack-plugins/webpack-less-plugin.js +19 -0
  127. package/.gitignore~ +0 -71
  128. package/news/4547.breaking~ +0 -1
  129. package/package.json~ +0 -444
  130. package/src/config/index.js~ +0 -223
@@ -145,6 +145,7 @@ class SharingComponent extends Component {
145
145
  this.onToggleInherit = this.onToggleInherit.bind(this);
146
146
  this.state = {
147
147
  search: '',
148
+ isLoading: false,
148
149
  inherit: props.inherit,
149
150
  entries: props.entries,
150
151
  isClient: false,
@@ -225,7 +226,17 @@ class SharingComponent extends Component {
225
226
  */
226
227
  onSearch(event) {
227
228
  event.preventDefault();
228
- this.props.getSharing(getBaseUrl(this.props.pathname), this.state.search);
229
+ this.setState({ isLoading: true });
230
+ this.props
231
+ .getSharing(getBaseUrl(this.props.pathname), this.state.search)
232
+ .then(() => {
233
+ this.setState({ isLoading: false });
234
+ })
235
+ .catch((error) => {
236
+ this.setState({ isLoading: false });
237
+ // eslint-disable-next-line no-console
238
+ console.error('Error searching users or groups', error);
239
+ });
229
240
  }
230
241
 
231
242
  /**
@@ -246,9 +257,9 @@ class SharingComponent extends Component {
246
257
  * @returns {undefined}
247
258
  */
248
259
  onToggleInherit() {
249
- this.setState({
250
- inherit: !this.state.inherit,
251
- });
260
+ this.setState((state) => ({
261
+ inherit: !state.inherit,
262
+ }));
252
263
  }
253
264
 
254
265
  /**
@@ -296,7 +307,10 @@ class SharingComponent extends Component {
296
307
  <Container id="page-sharing">
297
308
  <Helmet title={this.props.intl.formatMessage(messages.sharing)} />
298
309
  <Segment.Group raised>
299
- <Pluggable name="sharing-component" />
310
+ <Pluggable
311
+ name="sharing-component"
312
+ params={{ isLoading: this.state.isLoading }}
313
+ />
300
314
  <Plug pluggable="sharing-component" id="sharing-component-title">
301
315
  <Segment className="primary">
302
316
  <FormattedMessage
@@ -318,20 +332,29 @@ class SharingComponent extends Component {
318
332
  </Segment>
319
333
  </Plug>
320
334
  <Plug pluggable="sharing-component" id="sharing-component-search">
321
- <Segment>
322
- <Form onSubmit={this.onSearch}>
323
- <Form.Field>
324
- <Input
325
- name="SearchableText"
326
- action={{ icon: 'search' }}
327
- placeholder={this.props.intl.formatMessage(
328
- messages.searchForUserOrGroup,
329
- )}
330
- onChange={this.onChangeSearch}
331
- />
332
- </Form.Field>
333
- </Form>
334
- </Segment>
335
+ {({ isLoading }) => {
336
+ return (
337
+ <Segment>
338
+ <Form onSubmit={this.onSearch}>
339
+ <Form.Field>
340
+ <Input
341
+ name="SearchableText"
342
+ action={{
343
+ icon: 'search',
344
+ loading: isLoading,
345
+ disabled: isLoading,
346
+ }}
347
+ placeholder={this.props.intl.formatMessage(
348
+ messages.searchForUserOrGroup,
349
+ )}
350
+ onChange={this.onChangeSearch}
351
+ id="sharing-component-search"
352
+ />
353
+ </Form.Field>
354
+ </Form>
355
+ </Segment>
356
+ );
357
+ }}
335
358
  </Plug>
336
359
  <Plug
337
360
  pluggable="sharing-component"
@@ -404,9 +427,15 @@ class SharingComponent extends Component {
404
427
  <Segment attached>
405
428
  <Form.Field>
406
429
  <Checkbox
407
- checked={this.state.inherit}
430
+ id="inherit-permissions-checkbox"
431
+ name="inherit-permissions-checkbox"
432
+ defaultChecked={this.state.inherit}
408
433
  onChange={this.onToggleInherit}
409
- label={this.props.intl.formatMessage(messages.inherit)}
434
+ label={
435
+ <label htmlFor="inherit-permissions-checkbox">
436
+ {this.props.intl.formatMessage(messages.inherit)}
437
+ </label>
438
+ }
410
439
  />
411
440
  </Form.Field>
412
441
  <p className="help">
@@ -14,6 +14,7 @@ import {
14
14
  } from '@plone/volto/helpers/Url/Url';
15
15
 
16
16
  import config from '@plone/volto/registry';
17
+ import cx from 'classnames';
17
18
 
18
19
  const UniversalLink = ({
19
20
  href,
@@ -88,19 +89,16 @@ const UniversalLink = ({
88
89
  );
89
90
 
90
91
  if (isExternal) {
92
+ const isTelephoneOrMail = checkedURL.isMail || checkedURL.isTelephone;
91
93
  tag = (
92
94
  <a
93
95
  href={url}
94
96
  title={title}
95
97
  target={
96
- !checkedURL.isMail &&
97
- !checkedURL.isTelephone &&
98
- !(openLinkInNewTab === false)
99
- ? '_blank'
100
- : null
98
+ !isTelephoneOrMail && !(openLinkInNewTab === false) ? '_blank' : null
101
99
  }
102
100
  rel="noopener noreferrer"
103
- className={className}
101
+ className={cx({ external: !isTelephoneOrMail }, className)}
104
102
  {...props}
105
103
  >
106
104
  {children}
@@ -325,7 +325,9 @@ class ArrayWidget extends Component {
325
325
  : this.props.choices
326
326
  ? [
327
327
  ...choices,
328
- ...(this.props.noValueOption && !this.props.default
328
+ ...(this.props.noValueOption &&
329
+ (this.props.default === undefined ||
330
+ this.props.default === null)
329
331
  ? [
330
332
  {
331
333
  label: this.props.intl.formatMessage(
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import renderer from 'react-test-renderer';
3
3
  import configureStore from 'redux-mock-store';
4
4
  import { Provider } from 'react-intl-redux';
5
- import { waitFor } from '@testing-library/react';
5
+ import { waitFor, render, fireEvent } from '@testing-library/react';
6
6
 
7
7
  import ArrayWidget from './ArrayWidget';
8
8
 
@@ -41,3 +41,47 @@ test('renders an array widget component', async () => {
41
41
  await waitFor(() => {});
42
42
  expect(component.toJSON()).toMatchSnapshot();
43
43
  });
44
+
45
+ test("No 'No value' option when default value is 0", async () => {
46
+ const store = mockStore({
47
+ intl: {
48
+ locale: 'en',
49
+ messages: {},
50
+ },
51
+ });
52
+
53
+ const choices = [
54
+ ['0', 'None'],
55
+ ['1', 'One'],
56
+ ];
57
+
58
+ const value = {
59
+ value: '0',
60
+ label: 'None',
61
+ };
62
+
63
+ const _default = 0;
64
+
65
+ const { container } = render(
66
+ <Provider store={store}>
67
+ <ArrayWidget
68
+ id="my-field"
69
+ title="My field"
70
+ fieldSet="default"
71
+ choices={choices}
72
+ default={_default}
73
+ value={value}
74
+ noValueOption={true}
75
+ onChange={() => {}}
76
+ onBlur={() => {}}
77
+ onClick={() => {}}
78
+ />
79
+ </Provider>,
80
+ );
81
+
82
+ fireEvent.mouseDown(
83
+ container.querySelector('.react-select__dropdown-indicator'),
84
+ { button: 0 },
85
+ );
86
+ expect(container).toMatchSnapshot();
87
+ });
@@ -96,7 +96,7 @@ class FormFieldWrapper extends Component {
96
96
  {this.props.children}
97
97
 
98
98
  {map(error, (message) => (
99
- <Label key={message} basic color="red" pointing>
99
+ <Label key={message} basic color="red" className="form-error-label">
100
100
  {message}
101
101
  </Label>
102
102
  ))}
@@ -0,0 +1,210 @@
1
+ /**
2
+ * RegistryImageWidget component.
3
+ * @module components/manage/Widgets/RegistryImageWidget
4
+ */
5
+
6
+ import React from 'react';
7
+ import PropTypes from 'prop-types';
8
+ import { Button, Image, Dimmer } from 'semantic-ui-react';
9
+ import { readAsDataURL } from 'promise-file-reader';
10
+ import { injectIntl } from 'react-intl';
11
+ import deleteSVG from '@plone/volto/icons/delete.svg';
12
+ import { Icon, FormFieldWrapper } from '@plone/volto/components';
13
+ import loadable from '@loadable/component';
14
+ import { defineMessages, useIntl } from 'react-intl';
15
+ import { toPublicURL, validateFileUploadSize } from '@plone/volto/helpers';
16
+
17
+ const imageMimetypes = [
18
+ 'image/png',
19
+ 'image/jpeg',
20
+ 'image/webp',
21
+ 'image/jpg',
22
+ 'image/gif',
23
+ 'image/svg+xml',
24
+ ];
25
+ const Dropzone = loadable(() => import('react-dropzone'));
26
+
27
+ const messages = defineMessages({
28
+ releaseDrag: {
29
+ id: 'Drop files here ...',
30
+ defaultMessage: 'Drop files here ...',
31
+ },
32
+ editFile: {
33
+ id: 'Drop file here to replace the existing file',
34
+ defaultMessage: 'Drop file here to replace the existing file',
35
+ },
36
+ fileDrag: {
37
+ id: 'Drop file here to upload a new file',
38
+ defaultMessage: 'Drop file here to upload a new file',
39
+ },
40
+ replaceFile: {
41
+ id: 'Replace existing file',
42
+ defaultMessage: 'Replace existing file',
43
+ },
44
+ addNewFile: {
45
+ id: 'Choose a file',
46
+ defaultMessage: 'Choose a file',
47
+ },
48
+ });
49
+
50
+ /**
51
+ * RegistryImageWidget component class.
52
+ * @function RegistryImageWidget
53
+ * @returns {string} Markup of the component.
54
+ *
55
+ * To use it, in schema properties, declare a field like:
56
+ *
57
+ * ```jsx
58
+ * {
59
+ * title: "File",
60
+ * widget: 'file',
61
+ * }
62
+ * ```
63
+ * or:
64
+ *
65
+ * ```jsx
66
+ * {
67
+ * title: "File",
68
+ * type: 'object',
69
+ * }
70
+ * ```
71
+ *
72
+ */
73
+ const RegistryImageWidget = (props) => {
74
+ const { id, value, onChange, isDisabled } = props;
75
+ const intl = useIntl();
76
+
77
+ const fileName = value?.split(';')[0];
78
+ const imgsrc = fileName
79
+ ? `${toPublicURL('/')}@@site-logo/${atob(
80
+ fileName.replace('filenameb64:', ''),
81
+ )}`
82
+ : '';
83
+
84
+ /**
85
+ * Drop handler
86
+ * @method onDrop
87
+ * @param {array} files File objects
88
+ * @returns {undefined}
89
+ */
90
+ const onDrop = (files) => {
91
+ const file = files[0];
92
+ if (!validateFileUploadSize(file, intl.formatMessage)) return;
93
+
94
+ readAsDataURL(file).then((data) => {
95
+ const fields = data.match(/^data:(.*);(.*),(.*)$/);
96
+ onChange(id, `filenameb64:${btoa(file.name)};datab64:${fields[3]}}`);
97
+ });
98
+
99
+ let reader = new FileReader();
100
+ reader.onload = function () {
101
+ const fields = reader.result.match(/^data:(.*);(.*),(.*)$/);
102
+ if (imageMimetypes.includes(fields[1])) {
103
+ let imagePreview = document.getElementById(`field-${id}-image`);
104
+ imagePreview.src = reader.result;
105
+ }
106
+ };
107
+ reader.readAsDataURL(files[0]);
108
+ };
109
+
110
+ return (
111
+ <FormFieldWrapper {...props}>
112
+ <Dropzone onDrop={onDrop}>
113
+ {({ getRootProps, getInputProps, isDragActive }) => (
114
+ <div className="file-widget-dropzone" {...getRootProps()}>
115
+ {isDragActive && <Dimmer active></Dimmer>}
116
+ {imgsrc ? (
117
+ <Image
118
+ className="image-preview"
119
+ id={`field-${id}-image`}
120
+ size="small"
121
+ src={imgsrc}
122
+ />
123
+ ) : (
124
+ <div className="dropzone-placeholder">
125
+ {isDragActive ? (
126
+ <p className="dropzone-text">
127
+ {intl.formatMessage(messages.releaseDrag)}
128
+ </p>
129
+ ) : value ? (
130
+ <p className="dropzone-text">
131
+ {intl.formatMessage(messages.editFile)}
132
+ </p>
133
+ ) : (
134
+ <p className="dropzone-text">
135
+ {intl.formatMessage(messages.fileDrag)}
136
+ </p>
137
+ )}
138
+ </div>
139
+ )}
140
+
141
+ <label className="label-file-widget-input">
142
+ {value
143
+ ? intl.formatMessage(messages.replaceFile)
144
+ : intl.formatMessage(messages.addNewFile)}
145
+ </label>
146
+ <input
147
+ {...getInputProps({
148
+ type: 'file',
149
+ style: { display: 'none' },
150
+ })}
151
+ id={`field-${id}`}
152
+ name={id}
153
+ type="file"
154
+ disabled={isDisabled}
155
+ />
156
+ </div>
157
+ )}
158
+ </Dropzone>
159
+ <div className="field-file-name">
160
+ {value && (
161
+ <Button
162
+ icon
163
+ basic
164
+ className="delete-button"
165
+ aria-label="delete file"
166
+ disabled={isDisabled}
167
+ onClick={() => {
168
+ onChange(id, '');
169
+ }}
170
+ >
171
+ <Icon name={deleteSVG} size="20px" />
172
+ </Button>
173
+ )}
174
+ </div>
175
+ </FormFieldWrapper>
176
+ );
177
+ };
178
+
179
+ /**
180
+ * Property types.
181
+ * @property {Object} propTypes Property types.
182
+ * @static
183
+ */
184
+ RegistryImageWidget.propTypes = {
185
+ id: PropTypes.string.isRequired,
186
+ title: PropTypes.string.isRequired,
187
+ description: PropTypes.string,
188
+ required: PropTypes.bool,
189
+ error: PropTypes.arrayOf(PropTypes.string),
190
+ value: PropTypes.shape({
191
+ '@type': PropTypes.string,
192
+ title: PropTypes.string,
193
+ }),
194
+ onChange: PropTypes.func.isRequired,
195
+ wrapped: PropTypes.bool,
196
+ };
197
+
198
+ /**
199
+ * Default properties.
200
+ * @property {Object} defaultProps Default properties.
201
+ * @static
202
+ */
203
+ RegistryImageWidget.defaultProps = {
204
+ description: null,
205
+ required: false,
206
+ error: [],
207
+ value: null,
208
+ };
209
+
210
+ export default injectIntl(RegistryImageWidget);
@@ -0,0 +1,91 @@
1
+ import React from 'react';
2
+ import { Provider } from 'react-intl-redux';
3
+ import { render, waitFor } from '@testing-library/react';
4
+ import configureStore from 'redux-mock-store';
5
+
6
+ import RegistryImageWidget from './RegistryImageWidget';
7
+
8
+ import config from '@plone/volto/registry';
9
+
10
+ jest.spyOn(global.Date, 'now').mockImplementation(() => '0');
11
+
12
+ const mockStore = configureStore();
13
+
14
+ beforeAll(() => {
15
+ config.settings.publicURL = 'http://localhost:3000';
16
+ });
17
+
18
+ describe('RegistryImageWidget', () => {
19
+ test('renders an empty file widget component', async () => {
20
+ const store = mockStore({
21
+ intl: {
22
+ locale: 'en',
23
+ messages: {},
24
+ },
25
+ });
26
+
27
+ const { container } = render(
28
+ <Provider store={store}>
29
+ <RegistryImageWidget
30
+ id="my-field"
31
+ title="My field"
32
+ fieldSet="default"
33
+ onChange={() => {}}
34
+ />
35
+ </Provider>,
36
+ );
37
+
38
+ await waitFor(() => {});
39
+ expect(container).toMatchSnapshot();
40
+ });
41
+ test('renders a file widget component with value', async () => {
42
+ const store = mockStore({
43
+ intl: {
44
+ locale: 'en',
45
+ messages: {},
46
+ },
47
+ });
48
+
49
+ const { container } = render(
50
+ <Provider store={store}>
51
+ <RegistryImageWidget
52
+ id="my-field"
53
+ title="My field"
54
+ fieldSet="default"
55
+ onChange={() => {}}
56
+ value={
57
+ 'filenameb64:bG9nby5jYWI5NDVkOC5zdmc=;datab64:PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjE1OC4yNTNweCIgaGVpZ2h0PSI0MC42ODZweCIgdmlld0JveD0iMCAwIDE1OC4yNTMgNDAuNjg2IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxNTguMjUzIDQwLjY4NiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CiAgICAgICAgICAgICAgICAgICAgPGc+PHBhdGggZmlsbD0iIzAwODZDMyIgZD0iTTY1LjMyNywyMy4yMDhoLTYuNTg5djExLjM4OGgtNC4zOTNWNS42MzhoMTAuOTgxYzUuNjUzLDAsOS4yNzEsMy43NDIsOS4yNzEsOC43ODUgICAgICAgICAgICAgICAgIFM3MC45NzksMjMuMjA4LDY1LjMyNywyMy4yMDh6IE02NS4wODIsOS41ODNoLTYuMzQ1djkuNjM5aDYuMzQ1YzMuMDUsMCw1LjEyNC0xLjc0OSw1LjEyNC00Ljc5OSAgICAgICAgICAgICAgICAgQzcwLjIwNiwxMS4zNzIsNjguMTMyLDkuNTgzLDY1LjA4Miw5LjU4M3oiLz48cGF0aCBmaWxsPSIjMDA4NkMzIiBkPSJNODMuOTY5LDM0LjU5NmMtMy45MDQsMC01LjY1Mi0yLjY0NC01LjY1Mi01LjY5M1Y1LjYzOGg0LjE0OHYyMy4wMjFjMCwxLjU4NywwLjU2NywyLjM5OSwyLjIzNSwyLjM5OWgxLjgzICAgICAgICAgICAgICAgICB2My41MzhIODMuOTY5eiIvPjxwYXRoIGZpbGw9IiMwMDg2QzMiIGQ9Ik0xMDQuNzYyLDMyLjM5OWMtMS4zNDQsMS4zODQtMy4zNzcsMi40NC02LjE4NCwyLjQ0Yy0yLjgwNSwwLTQuNzk5LTEuMDU4LTYuMTQxLTIuNDQgICAgICAgICAgICAgICAgIGMtMS45NTEtMi4wMzItMi40MzktNC42MzctMi40MzktOC4xMzRjMC0zLjQ1NywwLjQ4OC02LjA2MSwyLjQzOS04LjA5NGMxLjM0Mi0xLjM4MywzLjMzNi0yLjQ0LDYuMTQxLTIuNDQgICAgICAgICAgICAgICAgIGMyLjgwNywwLDQuODQsMS4wNTksNi4xODQsMi40NGMxLjk1MSwyLjAzMywyLjQzOSw0LjYzNywyLjQzOSw4LjA5NEMxMDcuMjAzLDI3Ljc2MywxMDYuNzEzLDMwLjM2NiwxMDQuNzYyLDMyLjM5OXogICAgICAgICAgICAgICAgICBNMTAxLjYyOSwxOC42MTNjLTAuNzczLTAuNzczLTEuODMtMS4xODEtMy4wNTEtMS4xODFjLTEuMjE5LDAtMi4yMzYsMC40MDYtMy4wMSwxLjE4MWMtMS4yNiwxLjI2MS0xLjQyMiwzLjQxNi0xLjQyMiw1LjY1MiAgICAgICAgICAgICAgICAgczAuMTYyLDQuMzkzLDEuNDIyLDUuNjUzYzAuNzczLDAuNzcxLDEuNzkxLDEuMjIsMy4wMSwxLjIyYzEuMjIxLDAsMi4yNzctMC40NDcsMy4wNTEtMS4yMmMxLjI2Mi0xLjI2MiwxLjQyNC0zLjQxNywxLjQyNC01LjY1MyAgICAgICAgICAgICAgICAgUzEwMi44OTEsMTkuODczLDEwMS42MjksMTguNjEzeiIvPjxwYXRoIGZpbGw9IiMwMDg2QzMiIGQ9Ik0xMjMuNjQzLDM0LjU5NlYyMi4wMjljMC0zLjIxNC0xLjgzLTQuNTk3LTQuMTQ3LTQuNTk3cy00LjI3MSwxLjQyMy00LjI3MSw0LjU5N3YxMi41NjZoLTQuMTQ3di0yMC42MiAgICAgICAgICAgICAgICAgaDQuMDY1djIuMDc0YzEuNDI1LTEuNTQ2LDMuNDE2LTIuMzE4LDUuNDktMi4zMThjMi4xMTUsMCwzLjg2NSwwLjY5MSw1LjA4NCwxLjg3MWMxLjU4NiwxLjU0NSwyLjA3NCwzLjQ5NywyLjA3NCw1LjgxNXYxMy4xNzggICAgICAgICAgICAgICAgIEwxMjMuNjQzLDM0LjU5NkwxMjMuNjQzLDM0LjU5NnoiLz48cGF0aCBmaWxsPSIjMDA4NkMzIiBkPSJNMTM1Ljc3MiwyNS40ODZjMCwzLjUzNywxLjg3MSw1Ljc3NCw1LjI0Niw1Ljc3NGMyLjMxNywwLDMuNTM5LTAuNjQ5LDUuMDA0LTIuMTE1bDIuNjQzLDIuNDgxICAgICAgICAgICAgICAgICBjLTIuMTE1LDIuMTE0LTQuMTA3LDMuMjEzLTcuNzI3LDMuMjEzYy01LjE2NiwwLTkuMjczLTIuNzI1LTkuMjczLTEwLjU3NGMwLTYuNjcxLDMuNDU3LTEwLjUzNCw4Ljc0NC0xMC41MzQgICAgICAgICAgICAgICAgIGM1LjUzMSwwLDguNzQ0LDQuMDY3LDguNzQ0LDkuOTI1djEuODNIMTM1Ljc3MnogTTE0NC40NzUsMTkuNzkxYy0wLjY1LTEuNTQ1LTIuMTEzLTIuNjA0LTQuMDY2LTIuNjA0ICAgICAgICAgICAgICAgICBjLTEuOTUxLDAtMy40NTcsMS4wNTktNC4xMDcsMi42MDRjLTAuNDA2LDAuOTM2LTAuNDg4LDEuNTQ2LTAuNTI5LDIuODA3aDkuMjczQzE0NS4wMDMsMjEuMzM3LDE0NC44ODMsMjAuNzI2LDE0NC40NzUsMTkuNzkxeiIvPjxjaXJjbGUgZmlsbD0iIzAwODZDMyIgY3g9IjE3LjgxNSIgY3k9IjExLjUxNiIgcj0iNC40MDIiLz48cGF0aCBmaWxsPSIjMDA4NkMzIiBkPSJNMzEuMTY3LDIwLjMxMWMwLDIuNDMzLTEuOTY5LDQuNDAxLTQuNDAzLDQuNDAxYy0yLjQyNywwLTQuNDAxLTEuOTctNC40MDEtNC40MDEgICAgICAgICAgICAgICAgIGMwLTIuNDMzLDEuOTc1LTQuNDAxLDQuNDAxLTQuNDAxQzI5LjIsMTUuOTA5LDMxLjE2NywxNy44NzksMzEuMTY3LDIwLjMxMXoiLz48Y2lyY2xlIGZpbGw9IiMwMDg2QzMiIGN4PSIxNy44MDEiIGN5PSIyOS4xMzEiIHI9IjQuNDAyIi8+PGc+PHBhdGggZmlsbD0iIzAwODZDMyIgZD0iTTIwLjQ0MS0wLjA0NUM5LjIwNy0wLjA0NCwwLjEsOS4wNjMsMC4wOTksMjAuMjk4QzAuMSwzMS41MzIsOS4yMDcsNDAuNjM5LDIwLjQ0MSw0MC42NDEgICAgICAgICAgICAgICAgICAgICBjMTEuMjM1LTAuMDAyLDIwLjM0MS05LjEwNywyMC4zNDMtMjAuMzQzQzQwLjc4Myw5LjA2MywzMS42NzctMC4wNDQsMjAuNDQxLTAuMDQ1eiBNMzEuODkxLDMxLjc0NyAgICAgICAgICAgICAgICAgICAgIGMtMi45MzcsMi45MzQtNi45NzIsNC43NDItMTEuNDUsNC43NDNjLTQuNDc4LTAuMDAxLTguNTEzLTEuODExLTExLjQ1LTQuNzQzQzYuMDU4LDI4LjgxLDQuMjUsMjQuNzc1LDQuMjQ5LDIwLjI5OCAgICAgICAgICAgICAgICAgICAgIGMwLjAwMS00LjQ3OCwxLjgwOS04LjUxMyw0Ljc0My0xMS40NWMyLjkzNy0yLjkzNCw2Ljk3Mi00Ljc0MiwxMS40NS00Ljc0M2M0LjQ3OCwwLjAwMSw4LjUxMywxLjgxLDExLjQ1LDQuNzQzICAgICAgICAgICAgICAgICAgICAgYzIuOTM0LDIuOTM4LDQuNzQyLDYuOTczLDQuNzQzLDExLjQ1QzM2LjYzMywyNC43NzUsMzQuODI1LDI4LjgxLDMxLjg5MSwzMS43NDd6Ii8+PC9nPjxnPjxwYXRoIGZpbGw9IiMwMDg2QzMiIGQ9Ik0xNTMuOTg1LDkuOTVjLTEuMTk1LDAtMi4xNjQsMC45NzEtMi4xNjQsMi4xNjhjMC4wMDIsMS4xOTcsMC45NjksMi4xNjgsMi4xNjQsMi4xNjggICAgICAgICAgICAgICAgICAgICBjMS4xOTksMCwyLjE3Mi0wLjk3MSwyLjE3Mi0yLjE2OFMxNTUuMTg0LDkuOTUsMTUzLjk4NSw5Ljk1eiBNMTUzLjk4NSwxMy45NjhjLTEuMDIxLTAuMDAyLTEuODQ2LTAuODI3LTEuODQ2LTEuODUgICAgICAgICAgICAgICAgICAgICBjMC4wMDItMS4wMjEsMC44MjUtMS44NDksMS44NDYtMS44NTFjMS4wMjMsMC4wMDIsMS44NTIsMC44MjgsMS44NTQsMS44NTFDMTU1LjgzNiwxMy4xNDEsMTU1LjAwOCwxMy45NjYsMTUzLjk4NSwxMy45Njh6Ii8+PC9nPjxnPjxwYXRoIGZpbGw9IiMwMDg2QzMiIGQ9Ik0xNTQuNTA3LDEzLjQwOWwtMC41NC0xLjA4aC0wLjQ4NnYxLjA4aC0wLjM4OXYtMi41NjRoMC45OTRjMC40ODQsMCwwLjc5NiwwLjMxMywwLjc5NiwwLjc1ICAgICAgICAgICAgICAgICAgICAgYzAsMC4zNjctMC4yMjQsMC42MDItMC41MTMsMC42OGwwLjU5MiwxLjEzNkwxNTQuNTA3LDEzLjQwOUwxNTQuNTA3LDEzLjQwOXogTTE1NC4wNTYsMTEuMTk1aC0wLjU3NXYwLjgwM2gwLjU3NSBjMC4yNjEsMCwwLjQzNy0wLjE0NywwLjQzNy0wLjM5OVMxNTQuMzE3LDExLjE5NSwxNTQuMDU2LDExLjE5NXoiLz48L2c+PC9nPgogICAgICAgICAgICAgICAgICA8L3N2Zz4=}'
58
+ }
59
+ />
60
+ </Provider>,
61
+ );
62
+
63
+ await waitFor(() => {});
64
+ expect(container).toMatchSnapshot();
65
+ });
66
+ // test('renders a file widget component with value in raw data', async () => {
67
+ // const store = mockStore({
68
+ // intl: {
69
+ // locale: 'en',
70
+ // messages: {},
71
+ // },
72
+ // });
73
+
74
+ // const { container } = render(
75
+ // <Provider store={store}>
76
+ // <RegistryImageWidget
77
+ // id="my-field"
78
+ // title="My field"
79
+ // fieldSet="default"
80
+ // onChange={() => {}}
81
+ // value={
82
+ // 'filenameb64:bG9nby5jYWI5NDVkOC5zdmc=;datab64:PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjE1OC4yNTNweCIgaGVpZ2h0PSI0MC42ODZweCIgdmlld0JveD0iMCAwIDE1OC4yNTMgNDAuNjg2IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxNTguMjUzIDQwLjY4NiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CiAgICAgICAgICAgICAgICAgICAgPGc+PHBhdGggZmlsbD0iIzAwODZDMyIgZD0iTTY1LjMyNywyMy4yMDhoLTYuNTg5djExLjM4OGgtNC4zOTNWNS42MzhoMTAuOTgxYzUuNjUzLDAsOS4yNzEsMy43NDIsOS4yNzEsOC43ODUgICAgICAgICAgICAgICAgIFM3MC45NzksMjMuMjA4LDY1LjMyNywyMy4yMDh6IE02NS4wODIsOS41ODNoLTYuMzQ1djkuNjM5aDYuMzQ1YzMuMDUsMCw1LjEyNC0xLjc0OSw1LjEyNC00Ljc5OSAgICAgICAgICAgICAgICAgQzcwLjIwNiwxMS4zNzIsNjguMTMyLDkuNTgzLDY1LjA4Miw5LjU4M3oiLz48cGF0aCBmaWxsPSIjMDA4NkMzIiBkPSJNODMuOTY5LDM0LjU5NmMtMy45MDQsMC01LjY1Mi0yLjY0NC01LjY1Mi01LjY5M1Y1LjYzOGg0LjE0OHYyMy4wMjFjMCwxLjU4NywwLjU2NywyLjM5OSwyLjIzNSwyLjM5OWgxLjgzICAgICAgICAgICAgICAgICB2My41MzhIODMuOTY5eiIvPjxwYXRoIGZpbGw9IiMwMDg2QzMiIGQ9Ik0xMDQuNzYyLDMyLjM5OWMtMS4zNDQsMS4zODQtMy4zNzcsMi40NC02LjE4NCwyLjQ0Yy0yLjgwNSwwLTQuNzk5LTEuMDU4LTYuMTQxLTIuNDQgICAgICAgICAgICAgICAgIGMtMS45NTEtMi4wMzItMi40MzktNC42MzctMi40MzktOC4xMzRjMC0zLjQ1NywwLjQ4OC02LjA2MSwyLjQzOS04LjA5NGMxLjM0Mi0xLjM4MywzLjMzNi0yLjQ0LDYuMTQxLTIuNDQgICAgICAgICAgICAgICAgIGMyLjgwNywwLDQuODQsMS4wNTksNi4xODQsMi40NGMxLjk1MSwyLjAzMywyLjQzOSw0LjYzNywyLjQzOSw4LjA5NEMxMDcuMjAzLDI3Ljc2MywxMDYuNzEzLDMwLjM2NiwxMDQuNzYyLDMyLjM5OXogICAgICAgICAgICAgICAgICBNMTAxLjYyOSwxOC42MTNjLTAuNzczLTAuNzczLTEuODMtMS4xODEtMy4wNTEtMS4xODFjLTEuMjE5LDAtMi4yMzYsMC40MDYtMy4wMSwxLjE4MWMtMS4yNiwxLjI2MS0xLjQyMiwzLjQxNi0xLjQyMiw1LjY1MiAgICAgICAgICAgICAgICAgczAuMTYyLDQuMzkzLDEuNDIyLDUuNjUzYzAuNzczLDAuNzcxLDEuNzkxLDEuMjIsMy4wMSwxLjIyYzEuMjIxLDAsMi4yNzctMC40NDcsMy4wNTEtMS4yMmMxLjI2Mi0xLjI2MiwxLjQyNC0zLjQxNywxLjQyNC01LjY1MyAgICAgICAgICAgICAgICAgUzEwMi44OTEsMTkuODczLDEwMS42MjksMTguNjEzeiIvPjxwYXRoIGZpbGw9IiMwMDg2QzMiIGQ9Ik0xMjMuNjQzLDM0LjU5NlYyMi4wMjljMC0zLjIxNC0xLjgzLTQuNTk3LTQuMTQ3LTQuNTk3cy00LjI3MSwxLjQyMy00LjI3MSw0LjU5N3YxMi41NjZoLTQuMTQ3di0yMC42MiAgICAgICAgICAgICAgICAgaDQuMDY1djIuMDc0YzEuNDI1LTEuNTQ2LDMuNDE2LTIuMzE4LDUuNDktMi4zMThjMi4xMTUsMCwzLjg2NSwwLjY5MSw1LjA4NCwxLjg3MWMxLjU4NiwxLjU0NSwyLjA3NCwzLjQ5NywyLjA3NCw1LjgxNXYxMy4xNzggICAgICAgICAgICAgICAgIEwxMjMuNjQzLDM0LjU5NkwxMjMuNjQzLDM0LjU5NnoiLz48cGF0aCBmaWxsPSIjMDA4NkMzIiBkPSJNMTM1Ljc3MiwyNS40ODZjMCwzLjUzNywxLjg3MSw1Ljc3NCw1LjI0Niw1Ljc3NGMyLjMxNywwLDMuNTM5LTAuNjQ5LDUuMDA0LTIuMTE1bDIuNjQzLDIuNDgxICAgICAgICAgICAgICAgICBjLTIuMTE1LDIuMTE0LTQuMTA3LDMuMjEzLTcuNzI3LDMuMjEzYy01LjE2NiwwLTkuMjczLTIuNzI1LTkuMjczLTEwLjU3NGMwLTYuNjcxLDMuNDU3LTEwLjUzNCw4Ljc0NC0xMC41MzQgICAgICAgICAgICAgICAgIGM1LjUzMSwwLDguNzQ0LDQuMDY3LDguNzQ0LDkuOTI1djEuODNIMTM1Ljc3MnogTTE0NC40NzUsMTkuNzkxYy0wLjY1LTEuNTQ1LTIuMTEzLTIuNjA0LTQuMDY2LTIuNjA0ICAgICAgICAgICAgICAgICBjLTEuOTUxLDAtMy40NTcsMS4wNTktNC4xMDcsMi42MDRjLTAuNDA2LDAuOTM2LTAuNDg4LDEuNTQ2LTAuNTI5LDIuODA3aDkuMjczQzE0NS4wMDMsMjEuMzM3LDE0NC44ODMsMjAuNzI2LDE0NC40NzUsMTkuNzkxeiIvPjxjaXJjbGUgZmlsbD0iIzAwODZDMyIgY3g9IjE3LjgxNSIgY3k9IjExLjUxNiIgcj0iNC40MDIiLz48cGF0aCBmaWxsPSIjMDA4NkMzIiBkPSJNMzEuMTY3LDIwLjMxMWMwLDIuNDMzLTEuOTY5LDQuNDAxLTQuNDAzLDQuNDAxYy0yLjQyNywwLTQuNDAxLTEuOTctNC40MDEtNC40MDEgICAgICAgICAgICAgICAgIGMwLTIuNDMzLDEuOTc1LTQuNDAxLDQuNDAxLTQuNDAxQzI5LjIsMTUuOTA5LDMxLjE2NywxNy44NzksMzEuMTY3LDIwLjMxMXoiLz48Y2lyY2xlIGZpbGw9IiMwMDg2QzMiIGN4PSIxNy44MDEiIGN5PSIyOS4xMzEiIHI9IjQuNDAyIi8+PGc+PHBhdGggZmlsbD0iIzAwODZDMyIgZD0iTTIwLjQ0MS0wLjA0NUM5LjIwNy0wLjA0NCwwLjEsOS4wNjMsMC4wOTksMjAuMjk4QzAuMSwzMS41MzIsOS4yMDcsNDAuNjM5LDIwLjQ0MSw0MC42NDEgICAgICAgICAgICAgICAgICAgICBjMTEuMjM1LTAuMDAyLDIwLjM0MS05LjEwNywyMC4zNDMtMjAuMzQzQzQwLjc4Myw5LjA2MywzMS42NzctMC4wNDQsMjAuNDQxLTAuMDQ1eiBNMzEuODkxLDMxLjc0NyAgICAgICAgICAgICAgICAgICAgIGMtMi45MzcsMi45MzQtNi45NzIsNC43NDItMTEuNDUsNC43NDNjLTQuNDc4LTAuMDAxLTguNTEzLTEuODExLTExLjQ1LTQuNzQzQzYuMDU4LDI4LjgxLDQuMjUsMjQuNzc1LDQuMjQ5LDIwLjI5OCAgICAgICAgICAgICAgICAgICAgIGMwLjAwMS00LjQ3OCwxLjgwOS04LjUxMyw0Ljc0My0xMS40NWMyLjkzNy0yLjkzNCw2Ljk3Mi00Ljc0MiwxMS40NS00Ljc0M2M0LjQ3OCwwLjAwMSw4LjUxMywxLjgxLDExLjQ1LDQuNzQzICAgICAgICAgICAgICAgICAgICAgYzIuOTM0LDIuOTM4LDQuNzQyLDYuOTczLDQuNzQzLDExLjQ1QzM2LjYzMywyNC43NzUsMzQuODI1LDI4LjgxLDMxLjg5MSwzMS43NDd6Ii8+PC9nPjxnPjxwYXRoIGZpbGw9IiMwMDg2QzMiIGQ9Ik0xNTMuOTg1LDkuOTVjLTEuMTk1LDAtMi4xNjQsMC45NzEtMi4xNjQsMi4xNjhjMC4wMDIsMS4xOTcsMC45NjksMi4xNjgsMi4xNjQsMi4xNjggICAgICAgICAgICAgICAgICAgICBjMS4xOTksMCwyLjE3Mi0wLjk3MSwyLjE3Mi0yLjE2OFMxNTUuMTg0LDkuOTUsMTUzLjk4NSw5Ljk1eiBNMTUzLjk4NSwxMy45NjhjLTEuMDIxLTAuMDAyLTEuODQ2LTAuODI3LTEuODQ2LTEuODUgICAgICAgICAgICAgICAgICAgICBjMC4wMDItMS4wMjEsMC44MjUtMS44NDksMS44NDYtMS44NTFjMS4wMjMsMC4wMDIsMS44NTIsMC44MjgsMS44NTQsMS44NTFDMTU1LjgzNiwxMy4xNDEsMTU1LjAwOCwxMy45NjYsMTUzLjk4NSwxMy45Njh6Ii8+PC9nPjxnPjxwYXRoIGZpbGw9IiMwMDg2QzMiIGQ9Ik0xNTQuNTA3LDEzLjQwOWwtMC41NC0xLjA4aC0wLjQ4NnYxLjA4aC0wLjM4OXYtMi41NjRoMC45OTRjMC40ODQsMCwwLjc5NiwwLjMxMywwLjc5NiwwLjc1ICAgICAgICAgICAgICAgICAgICAgYzAsMC4zNjctMC4yMjQsMC42MDItMC41MTMsMC42OGwwLjU5MiwxLjEzNkwxNTQuNTA3LDEzLjQwOUwxNTQuNTA3LDEzLjQwOXogTTE1NC4wNTYsMTEuMTk1aC0wLjU3NXYwLjgwM2gwLjU3NSBjMC4yNjEsMCwwLjQzNy0wLjE0NywwLjQzNy0wLjM5OVMxNTQuMzE3LDExLjE5NSwxNTQuMDU2LDExLjE5NXoiLz48L2c+PC9nPgogICAgICAgICAgICAgICAgICA8L3N2Zz4=}'
83
+ // }
84
+ // />
85
+ // </Provider>,
86
+ // );
87
+
88
+ // await waitFor(() => {});
89
+ // expect(container).toMatchSnapshot();
90
+ // });
91
+ });
@@ -167,6 +167,19 @@ class SelectWidget extends Component {
167
167
  }
168
168
  }
169
169
 
170
+ componentDidUpdate(prevProps) {
171
+ if (
172
+ this.props.vocabBaseUrl !== prevProps.vocabBaseUrl &&
173
+ (!this.props.choices || this.props.choices?.length === 0)
174
+ ) {
175
+ this.props.getVocabulary({
176
+ vocabNameOrURL: this.props.vocabBaseUrl,
177
+ size: -1,
178
+ subrequest: this.props.lang,
179
+ });
180
+ }
181
+ }
182
+
170
183
  /**
171
184
  * Render method.
172
185
  * @method render
@@ -190,7 +203,8 @@ class SelectWidget extends Component {
190
203
  })),
191
204
  // Only set "no-value" option if there's no default in the field
192
205
  // TODO: also if this.props.defaultValue?
193
- ...(this.props.noValueOption && !this.props.default
206
+ ...(this.props.noValueOption &&
207
+ (this.props.default === undefined || this.props.default === null)
194
208
  ? [
195
209
  {
196
210
  label: this.props.intl.formatMessage(messages.no_value),
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import configureStore from 'redux-mock-store';
3
3
  import { Provider } from 'react-intl-redux';
4
- import { waitFor, render, screen } from '@testing-library/react';
4
+ import { waitFor, render, screen, fireEvent } from '@testing-library/react';
5
5
 
6
6
  import SelectWidget from './SelectWidget';
7
7
 
@@ -43,3 +43,47 @@ test('renders a select widget component', async () => {
43
43
  await waitFor(() => screen.getByText('My field'));
44
44
  expect(container).toMatchSnapshot();
45
45
  });
46
+
47
+ test("No 'No value' option when default value is 0", async () => {
48
+ const store = mockStore({
49
+ intl: {
50
+ locale: 'en',
51
+ messages: {},
52
+ },
53
+ });
54
+
55
+ const choices = [
56
+ ['0', 'None'],
57
+ ['1', 'One'],
58
+ ];
59
+
60
+ const value = {
61
+ value: '0',
62
+ label: 'None',
63
+ };
64
+
65
+ const _default = 0;
66
+
67
+ const { container } = render(
68
+ <Provider store={store}>
69
+ <SelectWidget
70
+ id="my-field"
71
+ title="My field"
72
+ fieldSet="default"
73
+ choices={choices}
74
+ default={_default}
75
+ value={value}
76
+ onChange={() => {}}
77
+ onBlur={() => {}}
78
+ onClick={() => {}}
79
+ />
80
+ </Provider>,
81
+ );
82
+
83
+ await waitFor(() => screen.getByText('None'));
84
+ fireEvent.mouseDown(
85
+ container.querySelector('.react-select__dropdown-indicator'),
86
+ { button: 0 },
87
+ );
88
+ expect(container).toMatchSnapshot();
89
+ });