@plone/volto 18.0.0-alpha.40 → 18.0.0-alpha.41

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.
package/CHANGELOG.md CHANGED
@@ -17,6 +17,25 @@ myst:
17
17
 
18
18
  <!-- towncrier release notes start -->
19
19
 
20
+ ## 18.0.0-alpha.41 (2024-07-05)
21
+
22
+ ### Breaking
23
+
24
+ - Fixed image widget position and look and feel in sidebar. @ichim-david
25
+
26
+ Breaking:
27
+ - Updated the markup of the widget in the sidebar to render the widget on a single column bellow the label.
28
+ - AddLink Pop-up of the widget is now rendered inside `toolbar-inner` instead of the document body, this fixes the positioning of the toolbar when scrolling. [#6159](https://github.com/plone/volto/issues/6159)
29
+
30
+ ### Bugfix
31
+
32
+ - Revisit login/logout process, better catching of edge cases @sneridagh [#6155](https://github.com/plone/volto/issues/6155)
33
+ - Restored browse link in `Slate` `AddLink` Pop-up. @ichim-david
34
+ Fixed recursive error when uploading an image using the `Image` widget. @sneridagh
35
+ Fixed image display when using an external URL. @sneridagh
36
+ Fixed the position of the `Image` widget toolbar when scrolling by changing the position of the toolbar to be within the widget area instead of the body. @ichim-david
37
+ Improved display of `AddLink` Pop-up when using it inside the `Image` widget where we don't have a link picker. @ichim-david [#6159](https://github.com/plone/volto/issues/6159)
38
+
20
39
  ## 18.0.0-alpha.40 (2024-07-03)
21
40
 
22
41
  ### Bugfix
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "18.0.0-alpha.40",
12
+ "version": "18.0.0-alpha.41",
13
13
  "repository": {
14
14
  "type": "git",
15
15
  "url": "git@github.com:plone/volto.git"
@@ -236,8 +236,8 @@
236
236
  "url": "^0.11.3",
237
237
  "use-deep-compare-effect": "1.8.1",
238
238
  "uuid": "^8.3.2",
239
- "@plone/volto-slate": "18.0.0-alpha.16",
240
239
  "@plone/registry": "1.7.0",
240
+ "@plone/volto-slate": "18.0.0-alpha.17",
241
241
  "@plone/scripts": "3.6.2"
242
242
  },
243
243
  "devDependencies": {
@@ -64,6 +64,7 @@ class AddLinkForm extends Component {
64
64
  };
65
65
 
66
66
  static defaultProps = {
67
+ objectBrowserPickerType: 'link',
67
68
  placeholder: 'Enter URL or select an item',
68
69
  };
69
70
 
@@ -246,7 +247,6 @@ class AddLinkForm extends Component {
246
247
  <div className="wrapper">
247
248
  <Input
248
249
  className={className}
249
- id={`field-link`}
250
250
  name="link"
251
251
  value={value || ''}
252
252
  onChange={({ target }) => this.onChange(target.value)}
@@ -16,36 +16,38 @@ import React from 'react';
16
16
  import { PositionedToolbar } from '@plone/volto-slate/editor/ui';
17
17
  import AddLinkForm from '@plone/volto/components/manage/AnchorPlugin/components/LinkButton/AddLinkForm';
18
18
 
19
- function getPositionStyle(el) {
20
- const rect = el.getBoundingClientRect();
21
-
22
- return {
23
- style: {
24
- opacity: 1,
25
- top: rect.top + window.pageYOffset - 6,
26
- left: rect.left + window.pageXOffset + rect.width / 2,
27
- },
28
- };
19
+ function getPositionStyle(position) {
20
+ return (
21
+ position || {
22
+ style: {
23
+ opacity: 1,
24
+ top: -5,
25
+ left: 55,
26
+ },
27
+ }
28
+ );
29
29
  }
30
30
 
31
31
  const useLinkEditor = () => {
32
32
  const [showLinkEditor, setShowLinkEditor] = React.useState(false);
33
33
  const show = React.useCallback(() => setShowLinkEditor(true), []);
34
- const savedPosition = React.useRef();
35
34
  const anchorNode = React.useRef();
36
35
 
37
- if (anchorNode.current) {
38
- savedPosition.current = getPositionStyle(anchorNode.current);
39
- }
40
-
41
36
  const LinkEditor = React.useCallback(
42
37
  (props) => {
43
- const { id, value, onChange, placeholder, objectBrowserPickerType } =
44
- props;
45
- return showLinkEditor && anchorNode.current && savedPosition.current ? (
38
+ const {
39
+ id,
40
+ value,
41
+ onChange,
42
+ placeholder,
43
+ objectBrowserPickerType,
44
+ position,
45
+ } = props;
46
+ return showLinkEditor && anchorNode.current ? (
46
47
  <PositionedToolbar
47
48
  className="add-link"
48
- position={savedPosition.current}
49
+ toggleButton={anchorNode.current}
50
+ position={getPositionStyle(position)}
49
51
  >
50
52
  <AddLinkForm
51
53
  placeholder={placeholder}
@@ -53,13 +55,11 @@ const useLinkEditor = () => {
53
55
  theme={{}}
54
56
  objectBrowserPickerType={objectBrowserPickerType}
55
57
  onChangeValue={(url) => {
56
- savedPosition.current = null;
57
58
  setShowLinkEditor(false);
58
59
  onChange(id, url);
59
60
  }}
60
61
  onClear={() => {}}
61
62
  onOverrideContent={(c) => {
62
- savedPosition.current = null;
63
63
  setShowLinkEditor(false);
64
64
  }}
65
65
  />
@@ -153,8 +153,8 @@ const InlineForm = (props) => {
153
153
  focus={index === focusIndex}
154
154
  value={formData[field]}
155
155
  required={schema.required.indexOf(field) !== -1}
156
- onChange={(id, value) => {
157
- onChangeField(id, value);
156
+ onChange={(id, value, itemInfo) => {
157
+ onChangeField(id, value, itemInfo);
158
158
  }}
159
159
  key={field}
160
160
  error={errors[field]}
@@ -14,6 +14,7 @@ import {
14
14
  getBaseUrl,
15
15
  isInternalURL,
16
16
  validateFileUploadSize,
17
+ usePrevious,
17
18
  } from '@plone/volto/helpers';
18
19
  import { createContent } from '@plone/volto/actions';
19
20
  import { readAsDataURL } from 'promise-file-reader';
@@ -91,13 +92,14 @@ const UnconnectedImageInput = (props) => {
91
92
 
92
93
  const requestId = `image-upload-${id}`;
93
94
 
94
- const { loading, loaded } = props.request;
95
+ const loaded = props.request.loaded;
95
96
  const { content } = props;
96
97
  const imageId = content?.['@id'];
97
98
  const image = content?.image;
99
+ let loading = false;
98
100
 
99
101
  useEffect(() => {
100
- if (uploading && !loading && loaded) {
102
+ if (uploading && loading && loaded) {
101
103
  setUploading(false);
102
104
  onChange(id, imageId, {
103
105
  image_field: 'image',
@@ -106,6 +108,8 @@ const UnconnectedImageInput = (props) => {
106
108
  }
107
109
  }, [loading, loaded, uploading, imageId, image, id, onChange]); // Explicitly list all dependencies
108
110
 
111
+ loading = usePrevious(props.request?.loading);
112
+
109
113
  const handleUpload = React.useCallback(
110
114
  (eventOrFile) => {
111
115
  if (restrictFileUpload === true) return;
@@ -161,7 +165,11 @@ const UnconnectedImageInput = (props) => {
161
165
  {selected && <ImageToolbar {...props} />}
162
166
  <img
163
167
  className={props.className}
164
- src={`${flattenToAppURL(value)}/@@images/image/${imageSize}`}
168
+ src={
169
+ isInternalURL(value)
170
+ ? `${flattenToAppURL(value)}/@@images/image/${imageSize}`
171
+ : value
172
+ }
165
173
  alt=""
166
174
  />
167
175
  </div>
@@ -302,10 +310,25 @@ export const ImageInput = compose(
302
310
  ),
303
311
  )(withObjectBrowser(UnconnectedImageInput));
304
312
 
305
- const ImageUploadWidget = (props) => (
306
- <FormFieldWrapper {...props} className="image-upload-widget">
307
- <ImageInput {...props} />
308
- </FormFieldWrapper>
309
- );
313
+ const ImageUploadWidget = (props) => {
314
+ const { fieldSet, id, title } = props;
315
+ return (
316
+ <FormFieldWrapper
317
+ {...props}
318
+ columns={1}
319
+ className="block image-upload-widget"
320
+ >
321
+ <div className="wrapper">
322
+ <label
323
+ id={`fieldset-${fieldSet}-field-label-${id}`}
324
+ htmlFor={`field-${id}`}
325
+ >
326
+ {title}
327
+ </label>
328
+ </div>
329
+ <ImageInput {...props} />
330
+ </FormFieldWrapper>
331
+ );
332
+ };
310
333
 
311
334
  export default ImageUploadWidget;
@@ -12,10 +12,15 @@ import {
12
12
  import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
13
13
  import qs from 'query-string';
14
14
 
15
- import { Helmet } from '@plone/volto/helpers';
15
+ import { Helmet, usePrevious } from '@plone/volto/helpers';
16
16
  import config from '@plone/volto/registry';
17
17
  import { Icon } from '@plone/volto/components';
18
- import { login, resetLoginRequest } from '@plone/volto/actions';
18
+ import {
19
+ login,
20
+ logout,
21
+ resetLoginRequest,
22
+ purgeMessages,
23
+ } from '@plone/volto/actions';
19
24
  import { toast } from 'react-toastify';
20
25
  import { Toast } from '@plone/volto/components';
21
26
  import aheadSVG from '@plone/volto/icons/ahead.svg';
@@ -78,8 +83,22 @@ const Login = (props) => {
78
83
  location.pathname.replace(/\/login\/?$/, '').replace(/\/logout\/?$/, '') ||
79
84
  '/';
80
85
 
86
+ const previousToken = usePrevious(token);
87
+
81
88
  useEffect(() => {
82
- if (token && !(props.isLogout || location?.state?.isLogout)) {
89
+ if (location?.state?.isLogout) {
90
+ // Execute a true Logout action
91
+ // This is needed to cover the use case of being logged in in
92
+ // another backend (eg. in development), having a token for
93
+ // localhost and try to use it, the login route has to know that
94
+ // it's the same as it comes from a logout
95
+ // See also Unauthorized.jsx
96
+ dispatch(logout());
97
+ dispatch(purgeMessages());
98
+ // Reset the location state
99
+ history.push(`${location.pathname}${location.search}`);
100
+ } else if (token && token !== previousToken) {
101
+ // We just did a true login action
83
102
  history.push(returnUrl || '/');
84
103
  if (toast.isActive('loggedOut')) {
85
104
  toast.dismiss('loggedOut');
@@ -116,8 +135,10 @@ const Login = (props) => {
116
135
  intl,
117
136
  history,
118
137
  returnUrl,
119
- props.isLogout,
138
+ location.search,
139
+ location.pathname,
120
140
  location?.state?.isLogout,
141
+ previousToken,
121
142
  ]);
122
143
 
123
144
  const onLogin = (event) => {
@@ -3,7 +3,7 @@ import { useDispatch, useSelector, shallowEqual } from 'react-redux';
3
3
  import { useHistory } from 'react-router-dom';
4
4
  import { defineMessages, useIntl } from 'react-intl';
5
5
  import qs from 'query-string';
6
- import { Login, Toast } from '@plone/volto/components';
6
+ import { Toast } from '@plone/volto/components';
7
7
  import { logout, purgeMessages } from '@plone/volto/actions';
8
8
  import { toast } from 'react-toastify';
9
9
 
@@ -55,7 +55,7 @@ const Logout = ({ location }) => {
55
55
  }
56
56
  }, [history, returnUrl, intl, token]);
57
57
 
58
- return <Login location={{ query: location.query }} isLogout={true} />;
58
+ return '';
59
59
  };
60
60
 
61
61
  export default Logout;
@@ -151,13 +151,13 @@
151
151
  .ui.form .inline.field .link-form-container {
152
152
  .wrapper {
153
153
  display: flex;
154
- border-bottom: 1px solid @lightGreyBorderColor;
155
154
 
156
155
  .ui.input.input-anchorlink-theme {
157
156
  vertical-align: unset;
158
157
 
159
158
  input {
160
- height: unset;
159
+ width: 100%;
160
+ height: unset; // input is only 40px and form.overrides sets height to 60px
161
161
  }
162
162
  }
163
163
  }
@@ -446,6 +446,7 @@ div.image-upload-widget-image {
446
446
  text-align: center;
447
447
 
448
448
  .toolbar-wrapper {
449
+ position: relative;
449
450
  display: flex;
450
451
  flex-direction: column;
451
452
  justify-content: flex-end;
@@ -726,10 +727,22 @@ div.image-upload-widget-image {
726
727
 
727
728
  .link-form-container {
728
729
  .inline.field .wrapper {
729
- min-height: initial;
730
+ width: 270px; // needed to ensure placeholder shows and buttons are aligned to the right
731
+ min-height: initial; // overrides .input.field .wrapper
732
+ justify-content: space-between;
730
733
  border-bottom: none;
731
734
  }
732
735
 
736
+ .ui.input-anchorlink-theme {
737
+ width: 100% !important; // overrides form.less width: auto
738
+ max-width: 81%; // needed so buttons don't shift to the right when value is present
739
+ margin-left: 0 !important; // overrides .block .toolbar-inner .ui.input
740
+ }
741
+
742
+ .ui.buttons {
743
+ width: auto; // for Add Link popup button to be aligned to the right
744
+ }
745
+
733
746
  button {
734
747
  padding: 0;
735
748
  border: 0;
@@ -743,10 +756,6 @@ div.image-upload-widget-image {
743
756
  outline: none;
744
757
  }
745
758
  }
746
-
747
- .ui.input {
748
- min-width: 250px;
749
- }
750
759
  }
751
760
 
752
761
  .blocks-chooser {
@@ -1,4 +1,4 @@
1
1
  export default Logout;
2
2
  declare function Logout({ location }: {
3
3
  location: any;
4
- }): import("react/jsx-runtime").JSX.Element;
4
+ }): string;