@plone/volto 18.0.0-alpha.41 → 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 (187) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/finalreleasechangelog.py +48 -0
  3. package/locales/ca/LC_MESSAGES/volto.po +39 -13
  4. package/locales/ca.json +1 -1
  5. package/locales/de/LC_MESSAGES/volto.po +40 -14
  6. package/locales/de.json +1 -1
  7. package/locales/en/LC_MESSAGES/volto.po +39 -13
  8. package/locales/en.json +1 -1
  9. package/locales/es/LC_MESSAGES/volto.po +40 -14
  10. package/locales/es.json +1 -1
  11. package/locales/eu/LC_MESSAGES/volto.po +40 -14
  12. package/locales/eu.json +1 -1
  13. package/locales/fi/LC_MESSAGES/volto.po +40 -14
  14. package/locales/fi.json +1 -1
  15. package/locales/fr/LC_MESSAGES/volto.po +40 -14
  16. package/locales/fr.json +1 -1
  17. package/locales/hi/LC_MESSAGES/volto.po +40 -14
  18. package/locales/hi.json +1 -1
  19. package/locales/it/LC_MESSAGES/volto.po +40 -14
  20. package/locales/it.json +1 -1
  21. package/locales/ja/LC_MESSAGES/volto.po +39 -13
  22. package/locales/ja.json +1 -1
  23. package/locales/nl/LC_MESSAGES/volto.po +39 -13
  24. package/locales/nl.json +1 -1
  25. package/locales/pt/LC_MESSAGES/volto.po +39 -13
  26. package/locales/pt.json +1 -1
  27. package/locales/pt_BR/LC_MESSAGES/volto.po +40 -14
  28. package/locales/pt_BR.json +1 -1
  29. package/locales/ro/LC_MESSAGES/volto.po +39 -13
  30. package/locales/ro.json +1 -1
  31. package/locales/volto.pot +40 -14
  32. package/locales/zh_CN/LC_MESSAGES/volto.po +40 -14
  33. package/locales/zh_CN.json +1 -1
  34. package/package.json +5 -6
  35. package/razzle.config.js +3 -3
  36. package/src/components/index.js +0 -1
  37. package/src/components/manage/Actions/Actions.stories.jsx +138 -0
  38. package/src/components/manage/Add/Add.jsx +7 -4
  39. package/src/components/manage/BlockChooser/BlockChooser.jsx +9 -1
  40. package/src/components/manage/Blocks/Block/BlocksForm.jsx +5 -0
  41. package/src/components/manage/Blocks/Block/Edit.jsx +24 -8
  42. package/src/components/manage/Blocks/Block/EditBlockWrapper.jsx +17 -1
  43. package/src/components/manage/Blocks/Block/Order/Item.jsx +8 -2
  44. package/src/components/manage/Blocks/Block/Order/Order.jsx +2 -0
  45. package/src/components/manage/Blocks/Container/Data.jsx +10 -2
  46. package/src/components/manage/Blocks/Grid/View.jsx +3 -0
  47. package/src/components/manage/Blocks/Image/ImageSidebar.jsx +10 -2
  48. package/src/components/manage/Blocks/LeadImage/Edit.jsx +74 -126
  49. package/src/components/manage/Blocks/Listing/ListingData.jsx +10 -2
  50. package/src/components/manage/Blocks/Maps/MapsSidebar.jsx +3 -1
  51. package/src/components/manage/Blocks/Search/SearchBlockEdit.jsx +2 -0
  52. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +18 -2
  53. package/src/components/manage/Blocks/Search/components/SortOn.jsx +82 -55
  54. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +1 -1
  55. package/src/components/manage/Blocks/Search/widgets/SelectMetadataField.jsx +107 -176
  56. package/src/components/manage/Blocks/Teaser/Data.jsx +10 -2
  57. package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +15 -8
  58. package/src/components/manage/Blocks/ToC/Edit.jsx +36 -28
  59. package/src/components/manage/Blocks/Video/Edit.jsx +105 -172
  60. package/src/components/manage/Blocks/Video/Edit.stories.jsx +57 -0
  61. package/src/components/manage/Blocks/Video/VideoSidebar.jsx +3 -1
  62. package/src/components/manage/Contents/Contents.jsx +4 -1
  63. package/src/components/manage/Contents/ContentsBreadcrumbs.stories.jsx +46 -0
  64. package/src/components/manage/Contents/ContentsPropertiesModal.jsx +85 -52
  65. package/src/components/manage/Contents/ContentsUploadModal.jsx +230 -323
  66. package/src/components/manage/Contents/ContentsUploadModal.stories.jsx +56 -0
  67. package/src/components/manage/Controlpanels/AddonsControlpanel.jsx +323 -441
  68. package/src/components/manage/Controlpanels/Aliases.jsx +452 -580
  69. package/src/components/manage/Controlpanels/Aliases.stories.jsx +74 -0
  70. package/src/components/manage/Controlpanels/ContentTypeSchema.jsx +1 -0
  71. package/src/components/manage/Controlpanels/Controlpanel.jsx +41 -2
  72. package/src/components/manage/Controlpanels/Controlpanel.test.jsx +55 -24
  73. package/src/components/manage/Controlpanels/DatabaseInformation.jsx +162 -229
  74. package/src/components/manage/Controlpanels/Groups/RenderGroups.jsx +74 -122
  75. package/src/components/manage/Controlpanels/UndoControlpanel.jsx +3 -3
  76. package/src/components/manage/Controlpanels/Users/UserGroupMembershipListing.jsx +28 -12
  77. package/src/components/manage/Controlpanels/Users/UserGroupMembershipMatrix.jsx +12 -4
  78. package/src/components/manage/Display/Display.jsx +92 -148
  79. package/src/components/manage/Display/Display.stories.jsx +46 -0
  80. package/src/components/manage/Edit/Edit.jsx +2 -4
  81. package/src/components/manage/Form/Form.jsx +85 -20
  82. package/src/components/manage/Form/InlineForm.jsx +2 -4
  83. package/src/components/manage/Form/ModalForm.jsx +1 -1
  84. package/src/components/manage/History/History.jsx +1 -1
  85. package/src/components/manage/Pluggable/Pluggable.test.js +1 -1
  86. package/src/components/manage/Preferences/ChangePassword.jsx +94 -172
  87. package/src/components/manage/Preferences/ChangePassword.stories.jsx +41 -0
  88. package/src/components/manage/Preferences/PersonalInformation.jsx +50 -115
  89. package/src/components/manage/Preferences/PersonalPreferences.jsx +46 -100
  90. package/src/components/manage/Preferences/PersonalPreferences.stories.jsx +48 -0
  91. package/src/components/manage/Toolbar/More.jsx +308 -399
  92. package/src/components/manage/Toolbar/Toolbar.jsx +1 -1
  93. package/src/components/manage/Widgets/ArrayWidget.jsx +2 -2
  94. package/src/components/manage/Widgets/DatetimeWidget.jsx +121 -175
  95. package/src/components/manage/Widgets/ImageWidget.jsx +6 -5
  96. package/src/components/manage/Widgets/RecurrenceWidget/EndField.jsx +7 -1
  97. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +80 -31
  98. package/src/components/manage/Widgets/ReferenceWidget.jsx +134 -210
  99. package/src/components/theme/Register/Register.jsx +70 -142
  100. package/src/components/theme/Register/Register.stories.jsx +49 -0
  101. package/src/components/theme/Search/Search.jsx +13 -5
  102. package/src/components/theme/Tags/Tags.jsx +19 -10
  103. package/src/components/theme/Tags/Tags.test.jsx +9 -11
  104. package/src/components/theme/View/AlbumView.jsx +122 -167
  105. package/src/components/theme/View/LinkView.jsx +4 -0
  106. package/src/components/theme/View/LinkView.test.jsx +2 -0
  107. package/src/components/theme/View/View.jsx +0 -13
  108. package/src/components/theme/View/View.test.jsx +0 -3
  109. package/src/config/ControlPanels.js +49 -43
  110. package/src/config/Widgets.jsx +1 -1
  111. package/src/config/config.test.js +1 -0
  112. package/src/config/index.js +23 -2
  113. package/src/config/slots.js +12 -0
  114. package/src/config/validation.ts +155 -0
  115. package/src/helpers/Blocks/Blocks.js +12 -7
  116. package/src/helpers/Blocks/Blocks.test.js +15 -0
  117. package/src/helpers/Blocks/cloneBlocks.ts +1 -1
  118. package/src/helpers/Extensions/withBlockExtensions.jsx +1 -1
  119. package/src/helpers/FormValidation/FormValidation.jsx +128 -172
  120. package/src/helpers/FormValidation/FormValidation.test.js +836 -8
  121. package/src/helpers/FormValidation/validators.ts +203 -0
  122. package/src/helpers/MessageLabels/MessageLabels.js +28 -0
  123. package/src/helpers/Url/Url.test.js +19 -6
  124. package/src/helpers/Url/urlRegex.js +1 -1
  125. package/src/helpers/User/User.js +1 -1
  126. package/src/helpers/index.js +2 -0
  127. package/src/hooks/client/useClient.js +1 -1
  128. package/src/middleware/api.js +4 -2
  129. package/src/middleware/index.js +1 -0
  130. package/src/middleware/userSessionReset.js +46 -0
  131. package/src/store.js +2 -0
  132. package/test-setup-config.jsx +10 -0
  133. package/theme/themes/default/modules/embed.variables +1 -1
  134. package/theme/themes/pastanaga/collections/form.overrides +34 -0
  135. package/theme/themes/pastanaga/extras/blocks.less +6 -0
  136. package/theme/themes/pastanaga/extras/sidebar.less +4 -0
  137. package/theme/themes/pastanaga/extras/toolbar.less +10 -3
  138. package/tsconfig.declarations.json +3 -2
  139. package/types/components/index.d.ts +0 -1
  140. package/types/components/manage/Actions/Actions.stories.d.ts +8 -0
  141. package/types/components/manage/Blocks/Block/Order/Order.d.ts +2 -1
  142. package/types/components/manage/Blocks/LeadImage/Edit.d.ts +14 -5
  143. package/types/components/manage/Blocks/Search/widgets/SelectMetadataField.d.ts +0 -5
  144. package/types/components/manage/Blocks/ToC/Edit.d.ts +1 -6
  145. package/types/components/manage/Blocks/Video/Edit.d.ts +1 -1
  146. package/types/components/manage/Blocks/Video/Edit.stories.d.ts +8 -0
  147. package/types/components/manage/Contents/ContentsBreadcrumbs.stories.d.ts +8 -0
  148. package/types/components/manage/Contents/ContentsUploadModal.d.ts +14 -2
  149. package/types/components/manage/Contents/ContentsUploadModal.stories.d.ts +8 -0
  150. package/types/components/manage/Contents/index.d.ts +1 -1
  151. package/types/components/manage/Controlpanels/AddonsControlpanel.d.ts +2 -2
  152. package/types/components/manage/Controlpanels/Aliases.d.ts +2 -2
  153. package/types/components/manage/Controlpanels/Aliases.stories.d.ts +8 -0
  154. package/types/components/manage/Controlpanels/DatabaseInformation.d.ts +2 -2
  155. package/types/components/manage/Controlpanels/Groups/RenderGroups.d.ts +10 -5
  156. package/types/components/manage/Controlpanels/index.d.ts +4 -4
  157. package/types/components/manage/Display/Display.stories.d.ts +8 -0
  158. package/types/components/manage/Preferences/ChangePassword.d.ts +2 -2
  159. package/types/components/manage/Preferences/ChangePassword.stories.d.ts +8 -0
  160. package/types/components/manage/Preferences/PersonalInformation.d.ts +7 -2
  161. package/types/components/manage/Preferences/PersonalPreferences.d.ts +5 -1
  162. package/types/components/manage/Preferences/PersonalPreferences.stories.d.ts +8 -0
  163. package/types/components/manage/Toolbar/More.d.ts +8 -5
  164. package/types/components/manage/Widgets/DatetimeWidget.d.ts +0 -85
  165. package/types/components/manage/Widgets/DatetimeWidget.stories.d.ts +0 -1
  166. package/types/components/manage/Widgets/ReferenceWidget.d.ts +27 -2
  167. package/types/components/manage/Widgets/index.d.ts +1 -1
  168. package/types/components/theme/Register/Register.d.ts +2 -2
  169. package/types/components/theme/Register/Register.stories.d.ts +9 -0
  170. package/types/components/theme/Tags/Tags.d.ts +15 -7
  171. package/types/components/theme/View/AlbumView.d.ts +3 -17
  172. package/types/config/ControlPanels.d.ts +8 -0
  173. package/types/config/RichTextEditor/ToHTML.d.ts +1 -1
  174. package/types/config/Widgets.d.ts +3 -3
  175. package/types/config/slots.d.ts +21 -0
  176. package/types/config/validation.d.ts +3 -0
  177. package/types/helpers/Blocks/Blocks.d.ts +6 -0
  178. package/types/helpers/Extensions/withBlockExtensions.d.ts +1 -1
  179. package/types/helpers/FormValidation/FormValidation.d.ts +2 -0
  180. package/types/helpers/FormValidation/validators.d.ts +29 -0
  181. package/types/helpers/MessageLabels/MessageLabels.d.ts +36 -0
  182. package/types/helpers/User/User.d.ts +1 -1
  183. package/types/helpers/index.d.ts +2 -2
  184. package/types/middleware/index.d.ts +1 -0
  185. package/types/middleware/userSessionReset.d.ts +5 -0
  186. package/src/components/theme/SocialSharing/SocialSharing.jsx +0 -48
  187. package/src/components/theme/SocialSharing/SocialSharing.test.jsx +0 -14
@@ -623,7 +623,7 @@ class Toolbar extends Component {
623
623
  aria-label={this.props.intl.formatMessage(
624
624
  messages.shrinkToolbar,
625
625
  )}
626
- className={cx({
626
+ className={cx('toolbar-handler-button', {
627
627
  [this.props.content?.review_state]:
628
628
  this.props.content?.review_state,
629
629
  })}
@@ -129,7 +129,7 @@ const compareOption = (inputValue = '', option, accessors) => {
129
129
  * @class ArrayWidget
130
130
  * @extends Component
131
131
  *
132
- * A createable select array widget will be rendered if the named vocabulary is
132
+ * A creatable select array widget will be rendered if the named vocabulary is
133
133
  * in the widget definition (hint) like:
134
134
  *
135
135
  * ```
@@ -280,7 +280,7 @@ class ArrayWidget extends Component {
280
280
  const { SortableContainer } = this.props.reactSortableHOC;
281
281
  const Select = this.props.reactSelect.default;
282
282
  const SortableSelect =
283
- // It will be only createable if the named vocabulary is in the widget definition
283
+ // It will be only creatable if the named vocabulary is in the widget definition
284
284
  // (hint) like:
285
285
  // list_field_voc_unconstrained = schema.List(
286
286
  // title=u"List field with values from vocabulary but not constrained to them.",
@@ -1,12 +1,6 @@
1
- /**
2
- * DatetimeWidget component.
3
- * @module components/manage/Widgets/DatetimeWidget
4
- */
5
- import React, { Component } from 'react';
6
- import { compose } from 'redux';
1
+ import React, { useState, useEffect } from 'react';
7
2
  import PropTypes from 'prop-types';
8
- import { defineMessages, injectIntl } from 'react-intl';
9
- import { connect } from 'react-redux';
3
+ import { defineMessages, useIntl } from 'react-intl';
10
4
  import loadable from '@loadable/component';
11
5
  import cx from 'classnames';
12
6
  import { Icon } from '@plone/volto/components';
@@ -50,6 +44,7 @@ const PrevIcon = () => (
50
44
  <Icon name={leftKey} size="30px" />
51
45
  </div>
52
46
  );
47
+
53
48
  const NextIcon = () => (
54
49
  <div
55
50
  style={{
@@ -72,68 +67,53 @@ const defaultTimeDateOnly = {
72
67
  second: 0,
73
68
  };
74
69
 
75
- /**
76
- * DatetimeWidget component class
77
- * @class DatetimeWidget
78
- * @extends Component
79
- *
80
- * To use it, in schema properties, declare a field like:
81
- *
82
- * ```jsx
83
- * {
84
- * title: "Publish date",
85
- * type: 'datetime',
86
- * }
87
- * ```
88
- */
89
- export class DatetimeWidgetComponent extends Component {
90
- /**
91
- * Constructor
92
- * @method constructor
93
- * @param {Object} props Component properties
94
- * @constructs DatetimeWidget
95
- */
96
- constructor(props) {
97
- super(props);
98
- this.moment = props.moment.default;
99
-
100
- this.state = {
101
- focused: false,
102
- // if passed value matches the construction time, we guess it's a default
103
- isDefault:
104
- parseDateTime(
105
- toBackendLang(this.props.lang),
106
- this.props.value,
107
- undefined,
108
- this.moment,
109
- )?.toISOString() === this.moment().utc().toISOString(),
110
- };
111
- }
112
-
113
- getInternalValue() {
114
- return parseDateTime(
115
- toBackendLang(this.props.lang),
116
- this.props.value,
70
+ const DatetimeWidgetComponent = (props) => {
71
+ const {
72
+ id,
73
+ resettable,
74
+ reactDates,
75
+ widgetOptions,
76
+ moment,
77
+ value,
78
+ onChange,
79
+ dateOnly,
80
+ widget,
81
+ noPastDates: propNoPastDates,
82
+ isDisabled,
83
+ } = props;
84
+
85
+ const intl = useIntl();
86
+ const lang = intl.locale;
87
+
88
+ const [focused, setFocused] = useState(false);
89
+ const [isDefault, setIsDefault] = useState(false);
90
+
91
+ const { SingleDatePicker } = reactDates;
92
+
93
+ useEffect(() => {
94
+ const parsedDateTime = parseDateTime(
95
+ toBackendLang(lang),
96
+ value,
117
97
  undefined,
118
- this.moment,
98
+ moment.default,
99
+ );
100
+ setIsDefault(
101
+ parsedDateTime?.toISOString() === moment.default().utc().toISOString(),
119
102
  );
120
- }
103
+ }, [value, lang, moment]);
121
104
 
122
- getDateOnly() {
123
- return this.props.dateOnly || this.props.widget === 'date';
124
- }
105
+ const getInternalValue = () => {
106
+ return parseDateTime(toBackendLang(lang), value, undefined, moment.default);
107
+ };
108
+
109
+ const getDateOnly = () => {
110
+ return dateOnly || widget === 'date';
111
+ };
125
112
 
126
- /**
127
- * Update date storage
128
- * @method onDateChange
129
- * @param {Object} date updated momentjs Object for date
130
- * @returns {undefined}
131
- */
132
- onDateChange = (date) => {
113
+ const onDateChange = (date) => {
133
114
  if (date) {
134
- const moment = this.props.moment.default;
135
- const isDateOnly = this.getDateOnly();
136
- const base = (this.getInternalValue() || moment()).set({
115
+ const isDateOnly = getDateOnly();
116
+ const base = (getInternalValue() || moment.default()).set({
137
117
  year: date.year(),
138
118
  month: date.month(),
139
119
  date: date.date(),
@@ -142,125 +122,100 @@ export class DatetimeWidgetComponent extends Component {
142
122
  const dateValue = isDateOnly
143
123
  ? base.format('YYYY-MM-DD')
144
124
  : base.toISOString();
145
- this.props.onChange(this.props.id, dateValue);
125
+ onChange(id, dateValue);
146
126
  }
147
- this.setState({ isDefault: false });
127
+ setIsDefault(false);
148
128
  };
149
129
 
150
- /**
151
- * Update date storage
152
- * @method onTimeChange
153
- * @param {Object} time updated momentjs Object for time
154
- * @returns {undefined}
155
- */
156
- onTimeChange = (time) => {
157
- const moment = this.props.moment.default;
130
+ const onTimeChange = (time) => {
158
131
  if (time) {
159
- const base = (this.getInternalValue() || moment()).set({
132
+ const base = (getInternalValue() || moment.default()).set({
160
133
  hours: time?.hours() ?? 0,
161
134
  minutes: time?.minutes() ?? 0,
162
135
  seconds: 0,
163
136
  });
164
137
  const dateValue = base.toISOString();
165
- this.props.onChange(this.props.id, dateValue);
138
+ onChange(id, dateValue);
166
139
  }
167
140
  };
168
141
 
169
- onResetDates = () => {
170
- this.setState({ isDefault: false });
171
- this.props.onChange(this.props.id, null);
142
+ const onResetDates = () => {
143
+ setIsDefault(false);
144
+ onChange(id, null);
172
145
  };
173
146
 
174
- /**
175
- * Handle SingleDatePicker focus
176
- * @method onFocusChange
177
- * @param {boolean} focused component focus state.
178
- * @returns {undefined}
179
- */
180
- onFocusChange = ({ focused }) => this.setState({ focused });
181
-
182
- render() {
183
- const { id, resettable, intl, reactDates, widgetOptions, lang } =
184
- this.props;
185
- const noPastDates =
186
- this.props.noPastDates || widgetOptions?.pattern_options?.noPastDates;
187
- const moment = this.props.moment.default;
188
- const datetime = this.getInternalValue();
189
- const dateOnly = this.getDateOnly();
190
- const { SingleDatePicker } = reactDates;
191
-
192
- return (
193
- <FormFieldWrapper {...this.props}>
194
- <div className="date-time-widget-wrapper">
147
+ const onFocusChange = ({ focused }) => setFocused(focused);
148
+
149
+ const noPastDates =
150
+ propNoPastDates || widgetOptions?.pattern_options?.noPastDates;
151
+ const datetime = getInternalValue();
152
+ const isDateOnly = getDateOnly();
153
+
154
+ return (
155
+ <FormFieldWrapper {...props}>
156
+ <div className="date-time-widget-wrapper">
157
+ <div
158
+ className={cx('ui input date-input', {
159
+ 'default-date': isDefault,
160
+ })}
161
+ >
162
+ <SingleDatePicker
163
+ date={datetime}
164
+ disabled={isDisabled}
165
+ onDateChange={onDateChange}
166
+ focused={focused}
167
+ numberOfMonths={1}
168
+ {...(noPastDates ? {} : { isOutsideRange: () => false })}
169
+ onFocusChange={onFocusChange}
170
+ noBorder
171
+ displayFormat={moment.default
172
+ .localeData(toBackendLang(lang))
173
+ .longDateFormat('L')}
174
+ navPrev={<PrevIcon />}
175
+ navNext={<NextIcon />}
176
+ id={`${id}-date`}
177
+ placeholder={intl.formatMessage(messages.date)}
178
+ />
179
+ </div>
180
+ {!isDateOnly && (
195
181
  <div
196
- className={cx('ui input date-input', {
197
- 'default-date': this.state.isDefault,
182
+ className={cx('ui input time-input', {
183
+ 'default-date': isDefault,
198
184
  })}
199
185
  >
200
- <SingleDatePicker
201
- date={datetime}
202
- disabled={this.props.isDisabled}
203
- onDateChange={this.onDateChange}
204
- focused={this.state.focused}
205
- numberOfMonths={1}
206
- {...(noPastDates ? {} : { isOutsideRange: () => false })}
207
- onFocusChange={this.onFocusChange}
208
- noBorder
209
- displayFormat={moment
186
+ <TimePicker
187
+ disabled={isDisabled}
188
+ defaultValue={datetime}
189
+ value={datetime}
190
+ onChange={onTimeChange}
191
+ allowEmpty={false}
192
+ showSecond={false}
193
+ use12Hours={lang === 'en'}
194
+ id={`${id}-time`}
195
+ format={moment.default
210
196
  .localeData(toBackendLang(lang))
211
- .longDateFormat('L')}
212
- navPrev={<PrevIcon />}
213
- navNext={<NextIcon />}
214
- id={`${id}-date`}
215
- placeholder={intl.formatMessage(messages.date)}
197
+ .longDateFormat('LT')}
198
+ placeholder={intl.formatMessage(messages.time)}
199
+ focusOnOpen
200
+ placement="bottomRight"
216
201
  />
217
202
  </div>
218
- {!dateOnly && (
219
- <div
220
- className={cx('ui input time-input', {
221
- 'default-date': this.state.isDefault,
222
- })}
223
- >
224
- <TimePicker
225
- disabled={this.props.isDisabled}
226
- defaultValue={datetime}
227
- value={datetime}
228
- onChange={this.onTimeChange}
229
- allowEmpty={false}
230
- showSecond={false}
231
- use12Hours={lang === 'en'}
232
- id={`${id}-time`}
233
- format={moment
234
- .localeData(toBackendLang(lang))
235
- .longDateFormat('LT')}
236
- placeholder={intl.formatMessage(messages.time)}
237
- focusOnOpen
238
- placement="bottomRight"
239
- />
240
- </div>
241
- )}
242
- {resettable && (
243
- <button
244
- // FF needs that the type is "button" in order to not POST the form
245
- type="button"
246
- disabled={this.props.isDisabled || !datetime}
247
- onClick={() => this.onResetDates()}
248
- className="item ui noborder button"
249
- >
250
- <Icon name={clearSVG} size="24px" className="close" />
251
- </button>
252
- )}
253
- </div>
254
- </FormFieldWrapper>
255
- );
256
- }
257
- }
203
+ )}
204
+ {resettable && (
205
+ <button
206
+ type="button"
207
+ disabled={isDisabled || !datetime}
208
+ onClick={onResetDates}
209
+ className="item ui noborder button"
210
+ >
211
+ <Icon name={clearSVG} size="24px" className="close" />
212
+ </button>
213
+ )}
214
+ </div>
215
+ </FormFieldWrapper>
216
+ );
217
+ };
258
218
 
259
- /**
260
- * Property types.
261
- * @property {Object} propTypes Property types.
262
- * @static
263
- */
264
219
  DatetimeWidgetComponent.propTypes = {
265
220
  id: PropTypes.string.isRequired,
266
221
  title: PropTypes.string.isRequired,
@@ -275,11 +230,6 @@ DatetimeWidgetComponent.propTypes = {
275
230
  resettable: PropTypes.bool,
276
231
  };
277
232
 
278
- /**
279
- * Default properties.
280
- * @property {Object} defaultProps Default properties.
281
- * @static
282
- */
283
233
  DatetimeWidgetComponent.defaultProps = {
284
234
  description: null,
285
235
  required: false,
@@ -290,10 +240,6 @@ DatetimeWidgetComponent.defaultProps = {
290
240
  resettable: true,
291
241
  };
292
242
 
293
- export default compose(
294
- injectLazyLibs(['reactDates', 'moment']),
295
- connect((state) => ({
296
- lang: state.intl.locale,
297
- })),
298
- injectIntl,
299
- )(DatetimeWidgetComponent);
243
+ export default injectLazyLibs(['reactDates', 'moment'])(
244
+ DatetimeWidgetComponent,
245
+ );
@@ -78,6 +78,7 @@ const UnconnectedImageInput = (props) => {
78
78
  placeholderLinkInput = '',
79
79
  onSelectItem,
80
80
  } = props;
81
+ const imageValue = value?.[0]?.['@id'] || value;
81
82
 
82
83
  const intl = useIntl();
83
84
  const linkEditor = useLinkEditor();
@@ -155,7 +156,7 @@ const UnconnectedImageInput = (props) => {
155
156
  }, [restrictFileUpload]);
156
157
  const onDragLeave = React.useCallback(() => setDragging(false), []);
157
158
 
158
- return value ? (
159
+ return imageValue ? (
159
160
  <div
160
161
  className="image-upload-widget-image"
161
162
  onClick={onFocus}
@@ -166,9 +167,9 @@ const UnconnectedImageInput = (props) => {
166
167
  <img
167
168
  className={props.className}
168
169
  src={
169
- isInternalURL(value)
170
- ? `${flattenToAppURL(value)}/@@images/image/${imageSize}`
171
- : value
170
+ isInternalURL(imageValue)
171
+ ? `${flattenToAppURL(imageValue)}/@@images/image/${imageSize}`
172
+ : imageValue
172
173
  }
173
174
  alt=""
174
175
  />
@@ -272,7 +273,7 @@ const UnconnectedImageInput = (props) => {
272
273
  </div>
273
274
  {linkEditor.anchorNode && (
274
275
  <linkEditor.LinkEditor
275
- value={value}
276
+ value={imageValue}
276
277
  placeholder={
277
278
  placeholderLinkInput ||
278
279
  intl.formatMessage(messages.linkAnImage)
@@ -81,7 +81,13 @@ const EndField = ({ value, count, until, onChange, intl }) => {
81
81
  id="until"
82
82
  title={intl.formatMessage(messages.recurrenceEndsUntil)}
83
83
  dateOnly={true}
84
- value={until || ''}
84
+ value={
85
+ until
86
+ ? typeof until === 'string'
87
+ ? until
88
+ : until?.toISOString()
89
+ : ''
90
+ }
85
91
  resettable={false}
86
92
  onChange={(id, value) => {
87
93
  onChange(id, value === '' ? undefined : value);
@@ -37,7 +37,6 @@ import {
37
37
  FREQUENCES,
38
38
  WEEKLY_DAYS,
39
39
  MONDAYFRIDAY_DAYS,
40
- toISOString,
41
40
  rrulei18n,
42
41
  } from './Utils';
43
42
 
@@ -219,26 +218,40 @@ class RecurrenceWidget extends Component {
219
218
 
220
219
  componentDidUpdate(prevProps) {
221
220
  if (this.props.value) {
222
- if (prevProps.formData?.start !== this.props.formData?.start) {
223
- let start = this.getUTCDate(this.props.formData?.start)
224
- .startOf('day')
225
- .toDate();
226
-
227
- this.setState((prevState) => {
228
- let rruleSet = prevState.rruleSet;
229
-
230
- rruleSet = this.updateRruleSet(
231
- rruleSet,
232
- prevState.formValues,
233
- 'dtstart',
234
- start,
235
- );
236
-
237
- return {
238
- ...prevState,
239
- rruleSet,
240
- };
241
- });
221
+ const changedStart =
222
+ prevProps.formData?.start !== this.props.formData?.start;
223
+ const changedEnd = prevProps.formData?.end !== this.props.formData?.end;
224
+
225
+ if (changedStart || changedEnd) {
226
+ let start = this.getUTCDate(this.props.formData?.start).toDate();
227
+
228
+ let changeFormValues = {};
229
+ if (changedEnd) {
230
+ changeFormValues.until = this.getUTCDate(
231
+ this.props.formData?.end,
232
+ ).toDate();
233
+ }
234
+ this.setState(
235
+ (prevState) => {
236
+ let rruleSet = prevState.rruleSet;
237
+
238
+ rruleSet = this.updateRruleSet(
239
+ rruleSet,
240
+ { ...prevState.formValues, ...changeFormValues },
241
+ 'dtstart',
242
+ start,
243
+ );
244
+
245
+ return {
246
+ ...prevState,
247
+ rruleSet,
248
+ };
249
+ },
250
+ () => {
251
+ //then, after set state, set recurrence rrule value
252
+ this.saveRrule();
253
+ },
254
+ );
242
255
  }
243
256
  }
244
257
  }
@@ -250,7 +263,8 @@ class RecurrenceWidget extends Component {
250
263
  setRecurrenceStartEnd = () => {
251
264
  const start = this.props.formData?.start;
252
265
 
253
- let _start = this.getUTCDate(start).startOf('day').toDate();
266
+ // The `start` date from Plone is in UTC
267
+ const _start = new Date(start);
254
268
 
255
269
  this.setState((prevState) => {
256
270
  let rruleSet = prevState.rruleSet;
@@ -339,7 +353,7 @@ class RecurrenceWidget extends Component {
339
353
  case 'until':
340
354
  if (value != null) {
341
355
  formValues['recurrenceEnds'] = option;
342
- formValues[option] = toISOString(value);
356
+ formValues[option] = value;
343
357
  }
344
358
  break;
345
359
  case 'byweekday':
@@ -422,7 +436,24 @@ class RecurrenceWidget extends Component {
422
436
  }
423
437
  break;
424
438
  case 'until':
425
- value = value ? this.moment(new Date(value)).utc().toDate() : null;
439
+ let mDate = null;
440
+ if (value) {
441
+ mDate = this.moment(new Date(value));
442
+ if (typeof value === 'string') {
443
+ mDate = this.moment(new Date(value));
444
+ } else {
445
+ //object-->Date()
446
+ mDate = this.moment(value);
447
+ }
448
+
449
+ if (this.props.formData.end) {
450
+ //set time from formData.end
451
+ const mEnd = this.moment(new Date(this.props.formData.end));
452
+ mDate.set('hour', mEnd.get('hour'));
453
+ mDate.set('minute', mEnd.get('minute'));
454
+ }
455
+ }
456
+ value = value ? mDate.toDate() : null;
426
457
  break;
427
458
  default:
428
459
  break;
@@ -447,7 +478,7 @@ class RecurrenceWidget extends Component {
447
478
  ? value
448
479
  : rruleSet.dtstart()
449
480
  ? rruleSet.dtstart()
450
- : this.moment().utc().toDate();
481
+ : new Date();
451
482
  var exdates =
452
483
  field === 'exdates' ? value : Object.assign([], rruleSet.exdates());
453
484
 
@@ -470,13 +501,14 @@ class RecurrenceWidget extends Component {
470
501
 
471
502
  getDefaultUntil = (freq) => {
472
503
  const moment = this.moment;
504
+
473
505
  var end = this.props.formData?.end
474
- ? toISOString(this.getUTCDate(this.props.formData.end).toDate())
506
+ ? moment(new Date(this.props.formData.end))
475
507
  : null;
476
- var tomorrow = toISOString(moment().add(1, 'days').utc().toDate());
477
- var nextWeek = toISOString(moment().add(7, 'days').utc().toDate());
478
- var nextMonth = toISOString(moment().add(1, 'months').utc().toDate());
479
- var nextYear = toISOString(moment().add(1, 'years').utc().toDate());
508
+ var tomorrow = moment().add(1, 'days');
509
+ var nextWeek = moment().add(7, 'days');
510
+ var nextMonth = moment().add(1, 'months');
511
+ var nextYear = moment().add(1, 'years');
480
512
 
481
513
  var until = end;
482
514
  switch (freq) {
@@ -502,6 +534,19 @@ class RecurrenceWidget extends Component {
502
534
  break;
503
535
  }
504
536
 
537
+ if (this.props.formData.end) {
538
+ //set default end time
539
+ until.set('hour', end.get('hour'));
540
+ until.set('minute', end.get('minute'));
541
+ }
542
+ until = new Date(
543
+ until.get('year'),
544
+ until.get('month'),
545
+ until.get('date'),
546
+ until.get('hour'),
547
+ until.get('minute'),
548
+ );
549
+
505
550
  return until;
506
551
  };
507
552
 
@@ -716,9 +761,13 @@ class RecurrenceWidget extends Component {
716
761
  }
717
762
  };
718
763
 
719
- save = () => {
764
+ saveRrule = () => {
720
765
  var value = this.state.rruleSet.toString();
721
766
  this.props.onChange(this.props.id, value);
767
+ };
768
+
769
+ save = () => {
770
+ this.saveRrule();
722
771
  this.close();
723
772
  };
724
773