@plone/volto 18.0.0-alpha.42 → 18.0.0-alpha.43

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 (137) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/finalreleasechangelog.py +48 -0
  3. package/locales/ca/LC_MESSAGES/volto.po +8 -12
  4. package/locales/ca.json +1 -1
  5. package/locales/de/LC_MESSAGES/volto.po +9 -13
  6. package/locales/de.json +1 -1
  7. package/locales/en/LC_MESSAGES/volto.po +8 -12
  8. package/locales/en.json +1 -1
  9. package/locales/es/LC_MESSAGES/volto.po +9 -13
  10. package/locales/es.json +1 -1
  11. package/locales/eu/LC_MESSAGES/volto.po +9 -13
  12. package/locales/eu.json +1 -1
  13. package/locales/fi/LC_MESSAGES/volto.po +9 -13
  14. package/locales/fi.json +1 -1
  15. package/locales/fr/LC_MESSAGES/volto.po +9 -13
  16. package/locales/fr.json +1 -1
  17. package/locales/hi/LC_MESSAGES/volto.po +9 -13
  18. package/locales/hi.json +1 -1
  19. package/locales/it/LC_MESSAGES/volto.po +9 -13
  20. package/locales/it.json +1 -1
  21. package/locales/ja/LC_MESSAGES/volto.po +8 -12
  22. package/locales/ja.json +1 -1
  23. package/locales/nl/LC_MESSAGES/volto.po +8 -12
  24. package/locales/nl.json +1 -1
  25. package/locales/pt/LC_MESSAGES/volto.po +8 -12
  26. package/locales/pt.json +1 -1
  27. package/locales/pt_BR/LC_MESSAGES/volto.po +9 -13
  28. package/locales/pt_BR.json +1 -1
  29. package/locales/ro/LC_MESSAGES/volto.po +8 -12
  30. package/locales/ro.json +1 -1
  31. package/locales/volto.pot +9 -13
  32. package/locales/zh_CN/LC_MESSAGES/volto.po +9 -13
  33. package/locales/zh_CN.json +1 -1
  34. package/package.json +3 -3
  35. package/razzle.config.js +1 -1
  36. package/src/components/manage/Actions/Actions.stories.jsx +138 -0
  37. package/src/components/manage/Add/Add.jsx +7 -4
  38. package/src/components/manage/BlockChooser/BlockChooser.jsx +9 -1
  39. package/src/components/manage/Blocks/Block/Edit.jsx +24 -8
  40. package/src/components/manage/Blocks/Block/EditBlockWrapper.jsx +17 -1
  41. package/src/components/manage/Blocks/Block/Order/Item.jsx +3 -1
  42. package/src/components/manage/Blocks/Grid/View.jsx +3 -0
  43. package/src/components/manage/Blocks/LeadImage/Edit.jsx +74 -126
  44. package/src/components/manage/Blocks/Search/components/SortOn.jsx +82 -55
  45. package/src/components/manage/Blocks/Search/widgets/SelectMetadataField.jsx +107 -176
  46. package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +15 -8
  47. package/src/components/manage/Blocks/ToC/Edit.jsx +36 -29
  48. package/src/components/manage/Blocks/Video/Edit.jsx +105 -172
  49. package/src/components/manage/Blocks/Video/Edit.stories.jsx +57 -0
  50. package/src/components/manage/Contents/Contents.jsx +3 -0
  51. package/src/components/manage/Contents/ContentsBreadcrumbs.stories.jsx +46 -0
  52. package/src/components/manage/Contents/ContentsPropertiesModal.jsx +85 -52
  53. package/src/components/manage/Contents/ContentsUploadModal.jsx +230 -323
  54. package/src/components/manage/Contents/ContentsUploadModal.stories.jsx +56 -0
  55. package/src/components/manage/Controlpanels/AddonsControlpanel.jsx +323 -441
  56. package/src/components/manage/Controlpanels/Aliases.jsx +452 -580
  57. package/src/components/manage/Controlpanels/Aliases.stories.jsx +74 -0
  58. package/src/components/manage/Controlpanels/Controlpanel.jsx +41 -2
  59. package/src/components/manage/Controlpanels/Controlpanel.test.jsx +55 -24
  60. package/src/components/manage/Controlpanels/DatabaseInformation.jsx +162 -229
  61. package/src/components/manage/Controlpanels/Groups/RenderGroups.jsx +74 -122
  62. package/src/components/manage/Display/Display.jsx +92 -148
  63. package/src/components/manage/Display/Display.stories.jsx +46 -0
  64. package/src/components/manage/Edit/Edit.jsx +2 -4
  65. package/src/components/manage/Preferences/ChangePassword.jsx +94 -172
  66. package/src/components/manage/Preferences/ChangePassword.stories.jsx +41 -0
  67. package/src/components/manage/Preferences/PersonalInformation.jsx +50 -115
  68. package/src/components/manage/Preferences/PersonalPreferences.jsx +46 -100
  69. package/src/components/manage/Preferences/PersonalPreferences.stories.jsx +48 -0
  70. package/src/components/manage/Toolbar/More.jsx +308 -399
  71. package/src/components/manage/Widgets/DatetimeWidget.jsx +121 -175
  72. package/src/components/manage/Widgets/ReferenceWidget.jsx +134 -210
  73. package/src/components/theme/Register/Register.jsx +70 -142
  74. package/src/components/theme/Register/Register.stories.jsx +49 -0
  75. package/src/components/theme/Tags/Tags.jsx +19 -10
  76. package/src/components/theme/Tags/Tags.test.jsx +9 -11
  77. package/src/components/theme/View/AlbumView.jsx +122 -167
  78. package/src/components/theme/View/LinkView.jsx +4 -0
  79. package/src/components/theme/View/LinkView.test.jsx +2 -0
  80. package/src/components/theme/View/View.jsx +0 -6
  81. package/src/config/ControlPanels.js +49 -43
  82. package/src/config/config.test.js +1 -0
  83. package/src/config/index.js +16 -0
  84. package/src/config/slots.js +12 -0
  85. package/src/helpers/Blocks/Blocks.js +12 -7
  86. package/src/helpers/Blocks/Blocks.test.js +15 -0
  87. package/src/helpers/Blocks/cloneBlocks.ts +1 -1
  88. package/src/helpers/FormValidation/FormValidation.jsx +19 -2
  89. package/src/helpers/FormValidation/validators.ts +1 -1
  90. package/src/helpers/Url/Url.test.js +15 -2
  91. package/src/helpers/Url/urlRegex.js +1 -1
  92. package/src/helpers/index.js +2 -0
  93. package/src/middleware/api.js +4 -2
  94. package/src/middleware/index.js +1 -0
  95. package/src/middleware/userSessionReset.js +46 -0
  96. package/src/store.js +2 -0
  97. package/test-setup-config.jsx +3 -0
  98. package/theme/themes/pastanaga/extras/blocks.less +6 -0
  99. package/types/components/manage/Actions/Actions.stories.d.ts +8 -0
  100. package/types/components/manage/Blocks/LeadImage/Edit.d.ts +14 -5
  101. package/types/components/manage/Blocks/Search/widgets/SelectMetadataField.d.ts +0 -5
  102. package/types/components/manage/Blocks/ToC/Edit.d.ts +1 -6
  103. package/types/components/manage/Blocks/Video/Edit.d.ts +1 -1
  104. package/types/components/manage/Blocks/Video/Edit.stories.d.ts +8 -0
  105. package/types/components/manage/Contents/ContentsBreadcrumbs.stories.d.ts +8 -0
  106. package/types/components/manage/Contents/ContentsUploadModal.d.ts +14 -2
  107. package/types/components/manage/Contents/ContentsUploadModal.stories.d.ts +8 -0
  108. package/types/components/manage/Contents/index.d.ts +1 -1
  109. package/types/components/manage/Controlpanels/AddonsControlpanel.d.ts +2 -2
  110. package/types/components/manage/Controlpanels/Aliases.d.ts +2 -2
  111. package/types/components/manage/Controlpanels/Aliases.stories.d.ts +8 -0
  112. package/types/components/manage/Controlpanels/DatabaseInformation.d.ts +2 -2
  113. package/types/components/manage/Controlpanels/Groups/RenderGroups.d.ts +10 -5
  114. package/types/components/manage/Controlpanels/index.d.ts +4 -4
  115. package/types/components/manage/Display/Display.stories.d.ts +8 -0
  116. package/types/components/manage/Preferences/ChangePassword.d.ts +2 -2
  117. package/types/components/manage/Preferences/ChangePassword.stories.d.ts +8 -0
  118. package/types/components/manage/Preferences/PersonalInformation.d.ts +7 -2
  119. package/types/components/manage/Preferences/PersonalPreferences.d.ts +5 -1
  120. package/types/components/manage/Preferences/PersonalPreferences.stories.d.ts +8 -0
  121. package/types/components/manage/Toolbar/More.d.ts +8 -5
  122. package/types/components/manage/Widgets/DatetimeWidget.d.ts +0 -85
  123. package/types/components/manage/Widgets/DatetimeWidget.stories.d.ts +0 -1
  124. package/types/components/manage/Widgets/ReferenceWidget.d.ts +27 -2
  125. package/types/components/manage/Widgets/index.d.ts +1 -1
  126. package/types/components/theme/Register/Register.d.ts +2 -2
  127. package/types/components/theme/Register/Register.stories.d.ts +9 -0
  128. package/types/components/theme/Tags/Tags.d.ts +15 -7
  129. package/types/components/theme/View/AlbumView.d.ts +3 -17
  130. package/types/config/ControlPanels.d.ts +8 -0
  131. package/types/config/Widgets.d.ts +1 -1
  132. package/types/config/slots.d.ts +21 -0
  133. package/types/helpers/Blocks/Blocks.d.ts +6 -0
  134. package/types/helpers/FormValidation/FormValidation.d.ts +1 -0
  135. package/types/helpers/index.d.ts +2 -2
  136. package/types/middleware/index.d.ts +1 -0
  137. package/types/middleware/userSessionReset.d.ts +5 -0
@@ -1,15 +1,8 @@
1
- /**
2
- * Edit image block.
3
- * @module components/manage/Blocks/Image/Edit
4
- */
5
-
6
- import React, { Component } from 'react';
1
+ import React from 'react';
7
2
  import PropTypes from 'prop-types';
8
- import { compose } from 'redux';
9
- import { defineMessages, injectIntl } from 'react-intl';
3
+ import { defineMessages, useIntl } from 'react-intl';
10
4
  import cx from 'classnames';
11
5
  import { Message } from 'semantic-ui-react';
12
- import { isEqual } from 'lodash';
13
6
 
14
7
  import { LeadImageSidebar, SidebarPortal } from '@plone/volto/components';
15
8
  import config from '@plone/volto/registry';
@@ -23,124 +16,79 @@ const messages = defineMessages({
23
16
  },
24
17
  });
25
18
 
26
- /**
27
- * Edit image block class.
28
- * @class Edit
29
- * @extends Component
30
- */
31
- class Edit extends Component {
32
- /**
33
- * Property types.
34
- * @property {Object} propTypes Property types.
35
- * @static
36
- */
37
- static propTypes = {
38
- properties: PropTypes.objectOf(PropTypes.any).isRequired,
39
- selected: PropTypes.bool.isRequired,
40
- block: PropTypes.string.isRequired,
41
- index: PropTypes.number.isRequired,
42
- data: PropTypes.objectOf(PropTypes.any).isRequired,
43
- pathname: PropTypes.string.isRequired,
44
- onChangeBlock: PropTypes.func.isRequired,
45
- openObjectBrowser: PropTypes.func.isRequired,
46
- };
47
-
48
- /**
49
- * Align block handler
50
- * @method onAlignBlock
51
- * @param {string} align Alignment option
52
- * @returns {undefined}
53
- */
54
- onAlignBlock(align) {
55
- this.props.onChangeBlock(this.props.block, {
56
- ...this.props.data,
57
- align,
58
- });
59
- }
60
-
61
- /**
62
- * @param {*} nextProps
63
- * @returns {boolean}
64
- * @memberof Edit
65
- */
66
- shouldComponentUpdate(nextProps) {
67
- return (
68
- this.props.selected ||
69
- nextProps.selected ||
70
- !isEqual(this.props.data, nextProps.data)
71
- );
72
- }
73
-
74
- node = React.createRef();
19
+ const Edit = (props) => {
20
+ const intl = useIntl();
21
+ const { data, properties, selected } = props;
75
22
 
76
- /**
77
- * Render method.
78
- * @method render
79
- * @returns {string} Markup for the component.
80
- */
81
- render() {
82
- const Image = config.getComponent({ name: 'Image' }).component;
83
- const { data, properties } = this.props;
84
- const placeholder =
85
- this.props.data.placeholder ||
86
- this.props.intl.formatMessage(messages.ImageBlockInputPlaceholder);
23
+ const placeholder = () =>
24
+ data.placeholder || intl.formatMessage(messages.ImageBlockInputPlaceholder);
87
25
 
88
- const hasImage = !!properties.image;
89
- const hasImageData = hasImage && !!properties.image.data;
90
- const className = cx('responsive', { 'full-image': data.align === 'full' });
91
- const altText = data.image_caption || properties.image_caption || '';
26
+ const Image = config.getComponent({ name: 'Image' }).component;
27
+ const hasImage = !!properties.image;
28
+ const hasImageData = hasImage && !!properties.image.data;
29
+ const className = cx('responsive', { 'full-image': data.align === 'full' });
30
+ const altText = data.image_caption || properties.image_caption || '';
92
31
 
93
- return (
94
- <div
95
- className={cx(
96
- 'block image align',
97
- {
98
- center: !Boolean(data.align),
99
- },
100
- data.align,
101
- )}
102
- >
103
- {!hasImage && (
104
- <Message>
105
- <center>
106
- <img src={imageBlockSVG} alt="" />
107
- <div className="message-text">{placeholder}</div>
108
- </center>
109
- </Message>
110
- )}
111
- {hasImage && hasImageData && (
112
- <img
113
- className={className}
114
- src={`data:${properties.image['content-type']};base64,${properties.image.data}`}
115
- width={properties.image.width}
116
- height={properties.image.height}
117
- alt={altText}
118
- style={{
119
- aspectRatio: `${properties.image.width}/${properties.image.height}`,
120
- }}
121
- />
122
- )}
123
- {hasImage && !hasImageData && (
124
- <Image
125
- className={className}
126
- item={properties}
127
- imageField="image"
128
- sizes={(() => {
129
- if (data.align === 'full' || data.align === 'center')
130
- return '100vw';
131
- if (data.align === 'left' || data.align === 'right')
132
- return '50vw';
133
- return undefined;
134
- })()}
135
- alt={altText}
136
- />
137
- )}
138
- <SidebarPortal selected={this.props.selected}>
139
- <LeadImageSidebar {...this.props} />
140
- </SidebarPortal>
141
- </div>
142
- );
143
- }
144
- }
32
+ return (
33
+ <div
34
+ className={cx(
35
+ 'block image align',
36
+ {
37
+ center: !Boolean(data.align),
38
+ },
39
+ data.align,
40
+ )}
41
+ >
42
+ {!hasImage && (
43
+ <Message>
44
+ <center>
45
+ <img src={imageBlockSVG} alt="" />
46
+ <div className="message-text">{placeholder}</div>
47
+ </center>
48
+ </Message>
49
+ )}
50
+ {hasImage && hasImageData && (
51
+ <img
52
+ className={className}
53
+ src={`data:${properties.image['content-type']};base64,${properties.image.data}`}
54
+ width={properties.image.width}
55
+ height={properties.image.height}
56
+ alt={altText}
57
+ style={{
58
+ aspectRatio: `${properties.image.width}/${properties.image.height}`,
59
+ }}
60
+ />
61
+ )}
62
+ {hasImage && !hasImageData && (
63
+ <Image
64
+ className={className}
65
+ item={properties}
66
+ imageField="image"
67
+ sizes={(() => {
68
+ return data.align === 'full' || data.align === 'center'
69
+ ? '100vw'
70
+ : data.align === 'left' || data.align === 'right'
71
+ ? '50vw'
72
+ : undefined;
73
+ })()}
74
+ alt={altText}
75
+ />
76
+ )}
77
+ <SidebarPortal selected={selected}>
78
+ <LeadImageSidebar {...props} />
79
+ </SidebarPortal>
80
+ </div>
81
+ );
82
+ };
145
83
 
146
- export default compose(injectIntl)(Edit);
84
+ export default Edit;
85
+ Edit.propTypes = {
86
+ properties: PropTypes.objectOf(PropTypes.any).isRequired,
87
+ selected: PropTypes.bool.isRequired,
88
+ block: PropTypes.string.isRequired,
89
+ index: PropTypes.number.isRequired,
90
+ data: PropTypes.objectOf(PropTypes.any).isRequired,
91
+ pathname: PropTypes.string.isRequired,
92
+ onChangeBlock: PropTypes.func.isRequired,
93
+ openObjectBrowser: PropTypes.func.isRequired,
94
+ };
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import { Button } from 'semantic-ui-react';
3
2
  import { defineMessages, injectIntl } from 'react-intl';
4
3
  import cx from 'classnames';
@@ -31,6 +30,10 @@ const messages = defineMessages({
31
30
  id: 'Descending',
32
31
  defaultMessage: 'Descending',
33
32
  },
33
+ sortedOn: {
34
+ id: 'Sorted on',
35
+ defaultMessage: 'Sorted on',
36
+ },
34
37
  });
35
38
 
36
39
  const SortOn = (props) => {
@@ -48,9 +51,17 @@ const SortOn = (props) => {
48
51
  const { sortable_indexes } = querystring;
49
52
  const Select = reactSelect.default;
50
53
 
51
- const activeSortOn = sortOn || data?.query?.sort_on || '';
54
+ const defaultSortOn = data?.query?.sort_on || '';
55
+ const activeSortOn = sortOn || defaultSortOn;
56
+
57
+ let { sortOnOptions = [] } = data;
58
+ sortOnOptions = [defaultSortOn, ...sortOnOptions];
59
+ sortOnOptions = [...new Set(sortOnOptions)];
52
60
 
53
- const { sortOnOptions = [] } = data;
61
+ const showSelectField = sortOnOptions.length > 1;
62
+ if (!showSelectField && !activeSortOn) {
63
+ return null;
64
+ }
54
65
  const value = {
55
66
  value: activeSortOn || intl.formatMessage(messages.noSelection),
56
67
  label:
@@ -62,59 +73,75 @@ const SortOn = (props) => {
62
73
  return (
63
74
  <div className="search-sort-wrapper">
64
75
  <div className="search-sort-on">
65
- <span className="sort-label">
66
- {intl.formatMessage(messages.sortOn)}
67
- </span>
68
- <Select
69
- id="select-search-sort-on"
70
- name="select-searchblock-sort-on"
71
- className="search-react-select-container"
72
- classNamePrefix="react-select"
73
- placeholder={intl.formatMessage(messages.sortOn)}
74
- styles={sortOnSelectStyles}
75
- theme={selectTheme}
76
- components={{ DropdownIndicator, Option }}
77
- options={[
78
- ...sortOnOptions.map((k) => ({
79
- value: k,
80
- label: sortable_indexes[k]?.title || k,
81
- })),
82
- ]}
83
- isSearchable={false}
84
- value={value}
85
- onChange={(data) => {
86
- !isEditMode && setSortOn(data.value);
87
- }}
88
- />
76
+ {showSelectField ? (
77
+ <>
78
+ <span className="sort-label">
79
+ {intl.formatMessage(messages.sortOn)}
80
+ </span>
81
+ <Select
82
+ id="select-search-sort-on"
83
+ name="select-searchblock-sort-on"
84
+ className="search-react-select-container"
85
+ classNamePrefix="react-select"
86
+ placeholder={intl.formatMessage(messages.sortOn)}
87
+ styles={sortOnSelectStyles}
88
+ theme={selectTheme}
89
+ components={{ DropdownIndicator, Option }}
90
+ options={[
91
+ ...sortOnOptions.map((k) => ({
92
+ value: k,
93
+ label:
94
+ sortable_indexes[k]?.title ||
95
+ k ||
96
+ intl.formatMessage(messages.noSelection),
97
+ })),
98
+ ]}
99
+ isSearchable={false}
100
+ value={value}
101
+ onChange={(data) => {
102
+ !isEditMode && setSortOn(data.value);
103
+ }}
104
+ />
105
+ </>
106
+ ) : (
107
+ <span className="sorted-label">
108
+ {intl.formatMessage(messages.sortedOn)}
109
+ <span className="sorted-label-value">{value.label}</span>
110
+ </span>
111
+ )}
89
112
  </div>
90
- <Button
91
- icon
92
- basic
93
- compact
94
- title={intl.formatMessage(messages.ascending)}
95
- className={cx({
96
- active: sortOrder === 'ascending',
97
- })}
98
- onClick={() => {
99
- !isEditMode && setSortOrder('ascending');
100
- }}
101
- >
102
- <Icon name={upSVG} size="25px" />
103
- </Button>
104
- <Button
105
- icon
106
- basic
107
- compact
108
- title={intl.formatMessage(messages.descending)}
109
- className={cx({
110
- active: sortOrder === 'descending',
111
- })}
112
- onClick={() => {
113
- !isEditMode && setSortOrder('descending');
114
- }}
115
- >
116
- <Icon name={downSVG} size="25px" />
117
- </Button>
113
+ {activeSortOn ? (
114
+ <>
115
+ <Button
116
+ icon
117
+ basic
118
+ compact
119
+ title={intl.formatMessage(messages.ascending)}
120
+ className={cx({
121
+ active: sortOrder === 'ascending',
122
+ })}
123
+ onClick={() => {
124
+ !isEditMode && setSortOrder('ascending');
125
+ }}
126
+ >
127
+ <Icon name={downSVG} size="25px" />
128
+ </Button>
129
+ <Button
130
+ icon
131
+ basic
132
+ compact
133
+ title={intl.formatMessage(messages.descending)}
134
+ className={cx({
135
+ active: sortOrder === 'descending',
136
+ })}
137
+ onClick={() => {
138
+ !isEditMode && setSortOrder('descending');
139
+ }}
140
+ >
141
+ <Icon name={upSVG} size="25px" />
142
+ </Button>
143
+ </>
144
+ ) : null}
118
145
  </div>
119
146
  );
120
147
  };
@@ -3,8 +3,8 @@
3
3
  * @module components/manage/Widgets/SelectWidget
4
4
  */
5
5
 
6
- import { map, intersection, filter, toPairs, groupBy } from 'lodash';
7
- import React, { Component } from 'react';
6
+ import { map, filter, toPairs, groupBy } from 'lodash';
7
+ import { useEffect } from 'react';
8
8
  import PropTypes from 'prop-types';
9
9
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
10
10
  import { compose } from 'redux';
@@ -30,187 +30,118 @@ const messages = defineMessages({
30
30
  });
31
31
 
32
32
  /**
33
- * SelectWidget component class.
33
+ * SelectWidget component function.
34
34
  * @function SelectWidget
35
35
  * @returns {string} Markup of the component.
36
36
  */
37
- class SelectWidget extends Component {
38
- /**
39
- * Property types.
40
- * @property {Object} propTypes Property types.
41
- * @static
42
- */
43
- static propTypes = {
44
- id: PropTypes.string.isRequired,
45
- title: PropTypes.string.isRequired,
46
- description: PropTypes.string,
47
- required: PropTypes.bool,
48
- error: PropTypes.arrayOf(PropTypes.string),
49
- loading: PropTypes.bool,
50
- value: PropTypes.oneOfType([
51
- PropTypes.object,
52
- PropTypes.string,
53
- PropTypes.bool,
54
- ]),
55
- onChange: PropTypes.func.isRequired,
56
- onBlur: PropTypes.func,
57
- onClick: PropTypes.func,
58
- onEdit: PropTypes.func,
59
- onDelete: PropTypes.func,
60
- wrapped: PropTypes.bool,
61
- querystring: PropTypes.object,
62
- };
37
+ const SelectWidget = (props) => {
38
+ const { choices, vocabBaseUrl, getVocabulary } = props;
63
39
 
64
- /**
65
- * Default properties
66
- * @property {Object} defaultProps Default properties.
67
- * @static
68
- */
69
- static defaultProps = {
70
- description: null,
71
- required: false,
72
- items: {
73
- vocabulary: null,
74
- },
75
- widgetOptions: {
76
- vocabulary: null,
77
- },
78
- error: [],
79
- choices: [],
80
- loading: false,
81
- value: null,
82
- onChange: () => {},
83
- onBlur: () => {},
84
- onClick: () => {},
85
- onEdit: null,
86
- onDelete: null,
87
- };
88
-
89
- state = {
90
- selectedOption: this.props.value
91
- ? { label: this.props.value.title, value: this.props.value.value }
92
- : {},
93
- };
94
-
95
- /**
96
- * Component did mount
97
- * @method componentDidMount
98
- * @returns {undefined}
99
- */
100
- componentDidMount() {
101
- if (!this.props.choices && this.props.vocabBaseUrl) {
102
- this.props.getVocabulary({ vocabNameOrURL: this.props.vocabBaseUrl });
40
+ useEffect(() => {
41
+ if (!choices && vocabBaseUrl) {
42
+ getVocabulary({ vocabNameOrURL: vocabBaseUrl });
103
43
  }
104
- }
105
-
106
- /**
107
- * Initiate search with new query
108
- * @method loadOptions
109
- * @param {string} search Search query.
110
- * @param {string} previousOptions The previous options rendered.
111
- * @param {string} additional Additional arguments to pass to the next loadOptions.
112
- * @returns {undefined}
113
- */
114
- loadOptions = (search, previousOptions, additional) => {
115
- let hasMore = this.props.itemsTotal > previousOptions.length;
116
- if (hasMore) {
117
- const offset = this.state.search !== search ? 0 : additional.offset;
118
- this.props.getVocabulary({
119
- vocabNameOrURL: this.props.vocabBaseUrl,
120
- query: search,
121
- start: offset,
122
- });
123
- this.setState({ search });
124
-
125
- return {
126
- options:
127
- intersection(previousOptions, this.props.choices).length ===
128
- this.props.choices.length
129
- ? []
130
- : this.props.choices,
131
- hasMore: hasMore,
132
- additional: {
133
- offset: offset === additional.offset ? offset + 25 : offset,
134
- },
135
- };
136
- }
137
- return null;
138
- };
139
-
140
- /* Customized to pass object instead of plain string value */
141
- handleChange = (selectedOption) => {
142
- this.setState({ selectedOption });
143
- this.props.onChange(this.props.id, {
144
- value: selectedOption.value,
145
- title: selectedOption.label,
146
- });
147
- };
148
-
149
- /**
150
- * Render method.
151
- * @method render
152
- * @returns {string} Markup for the component.
153
- */
154
- render() {
155
- const {
156
- id,
157
- // choices,
158
- value,
159
- onChange,
160
- placeholder,
161
- querystring,
162
- filterOptions = identity,
163
- } = this.props;
164
- const isDisabled = false;
165
- const { indexes = [] } = querystring;
166
-
167
- const Select = this.props.reactSelect.default;
168
-
169
- return (
170
- <FormFieldWrapper {...this.props}>
171
- <Select
172
- id={`field-${id}`}
173
- name={id}
174
- placeholder={
175
- placeholder ?? this.props.intl.formatMessage(messages.select)
176
- }
177
- isDisabled={isDisabled}
178
- className="react-select-container"
179
- classNamePrefix="react-select"
180
- options={map(
181
- toPairs(
182
- groupBy(toPairs(filterOptions(indexes)), (item) => item[1].group),
44
+ }, [choices, vocabBaseUrl, getVocabulary]);
45
+
46
+ const {
47
+ id,
48
+ value,
49
+ onChange,
50
+ placeholder,
51
+ querystring,
52
+ filterOptions = identity,
53
+ } = props;
54
+
55
+ const isDisabled = false;
56
+ const { indexes = [] } = querystring;
57
+ const Select = props.reactSelect.default;
58
+
59
+ return (
60
+ <FormFieldWrapper {...props}>
61
+ <Select
62
+ id={`field-${id}`}
63
+ name={id}
64
+ placeholder={placeholder ?? props.intl.formatMessage(messages.select)}
65
+ isDisabled={isDisabled}
66
+ className="react-select-container"
67
+ classNamePrefix="react-select"
68
+ options={map(
69
+ toPairs(
70
+ groupBy(toPairs(filterOptions(indexes)), (item) => item[1].group),
71
+ ),
72
+ (group) => ({
73
+ label: group[0],
74
+ options: map(
75
+ filter(group[1], (item) => item[1].enabled),
76
+ (field) => ({
77
+ label: field[1].title,
78
+ value: field[0],
79
+ }),
183
80
  ),
184
- (group) => ({
185
- label: group[0],
186
- options: map(
187
- filter(group[1], (item) => item[1].enabled),
188
- (field) => ({
189
- label: field[1].title,
190
- value: field[0],
191
- }),
192
- ),
193
- }),
194
- )}
195
- styles={customSelectStyles}
196
- theme={selectTheme}
197
- components={{ DropdownIndicator, Option }}
198
- value={{ value: value?.value, label: indexes[value?.value]?.title }}
199
- onChange={(data) => {
200
- let dataValue = [];
201
- if (Array.isArray(data)) {
202
- for (let obj of data) {
203
- dataValue.push(obj.value);
204
- }
205
- return onChange(id, dataValue);
81
+ }),
82
+ )}
83
+ styles={customSelectStyles}
84
+ theme={selectTheme}
85
+ components={{ DropdownIndicator, Option }}
86
+ value={{
87
+ value: value?.value,
88
+ label: indexes[value?.value]?.title,
89
+ }}
90
+ onChange={(data) => {
91
+ let dataValue = [];
92
+ if (Array.isArray(data)) {
93
+ for (let obj of data) {
94
+ dataValue.push(obj.value);
206
95
  }
207
- return onChange(id, data);
208
- }}
209
- />
210
- </FormFieldWrapper>
211
- );
212
- }
213
- }
96
+ return onChange(id, dataValue);
97
+ }
98
+ return onChange(id, data);
99
+ }}
100
+ />
101
+ </FormFieldWrapper>
102
+ );
103
+ };
104
+
105
+ SelectWidget.propTypes = {
106
+ id: PropTypes.string.isRequired,
107
+ title: PropTypes.string.isRequired,
108
+ description: PropTypes.string,
109
+ required: PropTypes.bool,
110
+ error: PropTypes.arrayOf(PropTypes.string),
111
+ loading: PropTypes.bool,
112
+ value: PropTypes.oneOfType([
113
+ PropTypes.object,
114
+ PropTypes.string,
115
+ PropTypes.bool,
116
+ ]),
117
+ onChange: PropTypes.func.isRequired,
118
+ onBlur: PropTypes.func,
119
+ onClick: PropTypes.func,
120
+ onEdit: PropTypes.func,
121
+ onDelete: PropTypes.func,
122
+ wrapped: PropTypes.bool,
123
+ querystring: PropTypes.object,
124
+ };
125
+
126
+ SelectWidget.defaultProps = {
127
+ description: null,
128
+ required: false,
129
+ items: {
130
+ vocabulary: null,
131
+ },
132
+ widgetOptions: {
133
+ vocabulary: null,
134
+ },
135
+ error: [],
136
+ choices: [],
137
+ loading: false,
138
+ value: null,
139
+ onChange: () => {},
140
+ onBlur: () => {},
141
+ onClick: () => {},
142
+ onEdit: null,
143
+ onDelete: null,
144
+ };
214
145
 
215
146
  export default compose(
216
147
  withQueryString,