@plone/volto 19.0.0-alpha.29 → 19.0.0-alpha.30
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 +37 -0
- package/locales/af/LC_MESSAGES/volto.po +45 -0
- package/locales/af.json +1 -1
- package/locales/ar/LC_MESSAGES/volto.po +45 -0
- package/locales/ar.json +1 -1
- package/locales/bg/LC_MESSAGES/volto.po +45 -0
- package/locales/bg.json +1 -1
- package/locales/bn/LC_MESSAGES/volto.po +45 -0
- package/locales/bn.json +1 -1
- package/locales/ca/LC_MESSAGES/volto.po +45 -0
- package/locales/ca.json +1 -1
- package/locales/cs/LC_MESSAGES/volto.po +45 -0
- package/locales/cs.json +1 -1
- package/locales/cy/LC_MESSAGES/volto.po +45 -0
- package/locales/cy.json +1 -1
- package/locales/da/LC_MESSAGES/volto.po +45 -0
- package/locales/da.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +45 -0
- package/locales/de.json +1 -1
- package/locales/el/LC_MESSAGES/volto.po +45 -0
- package/locales/el.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +45 -0
- package/locales/en.json +1 -1
- package/locales/en_AU/LC_MESSAGES/volto.po +45 -0
- package/locales/en_AU.json +1 -1
- package/locales/en_GB/LC_MESSAGES/volto.po +45 -0
- package/locales/en_GB.json +1 -1
- package/locales/eo/LC_MESSAGES/volto.po +45 -0
- package/locales/eo.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +45 -0
- package/locales/es.json +1 -1
- package/locales/et/LC_MESSAGES/volto.po +45 -0
- package/locales/et.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +45 -0
- package/locales/eu.json +1 -1
- package/locales/fa/LC_MESSAGES/volto.po +45 -0
- package/locales/fa.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +45 -0
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +45 -0
- package/locales/fr.json +1 -1
- package/locales/fu/LC_MESSAGES/volto.po +45 -0
- package/locales/fu.json +1 -1
- package/locales/gl/LC_MESSAGES/volto.po +45 -0
- package/locales/gl.json +1 -1
- package/locales/he/LC_MESSAGES/volto.po +45 -0
- package/locales/he.json +1 -1
- package/locales/hi/LC_MESSAGES/volto.po +45 -0
- package/locales/hi.json +1 -1
- package/locales/hr/LC_MESSAGES/volto.po +45 -0
- package/locales/hr.json +1 -1
- package/locales/hu/LC_MESSAGES/volto.po +45 -0
- package/locales/hu.json +1 -1
- package/locales/hy/LC_MESSAGES/volto.po +45 -0
- package/locales/hy.json +1 -1
- package/locales/id/LC_MESSAGES/volto.po +45 -0
- package/locales/id.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +45 -0
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +45 -0
- package/locales/ja.json +1 -1
- package/locales/ka/LC_MESSAGES/volto.po +45 -0
- package/locales/ka.json +1 -1
- package/locales/kn/LC_MESSAGES/volto.po +45 -0
- package/locales/kn.json +1 -1
- package/locales/ko/LC_MESSAGES/volto.po +45 -0
- package/locales/ko.json +1 -1
- package/locales/lt/LC_MESSAGES/volto.po +45 -0
- package/locales/lt.json +1 -1
- package/locales/lv/LC_MESSAGES/volto.po +45 -0
- package/locales/lv.json +1 -1
- package/locales/mi/LC_MESSAGES/volto.po +45 -0
- package/locales/mi.json +1 -1
- package/locales/mk/LC_MESSAGES/volto.po +45 -0
- package/locales/mk.json +1 -1
- package/locales/my/LC_MESSAGES/volto.po +45 -0
- package/locales/my.json +1 -1
- package/locales/nb_NO/LC_MESSAGES/volto.po +45 -0
- package/locales/nb_NO.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +45 -0
- package/locales/nl.json +1 -1
- package/locales/nn/LC_MESSAGES/volto.po +45 -0
- package/locales/nn.json +1 -1
- package/locales/pl/LC_MESSAGES/volto.po +45 -0
- package/locales/pl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +45 -0
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +45 -0
- package/locales/pt_BR.json +1 -1
- package/locales/rm/LC_MESSAGES/volto.po +45 -0
- package/locales/rm.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +100 -55
- package/locales/ro.json +1 -1
- package/locales/ru/LC_MESSAGES/volto.po +45 -0
- package/locales/ru.json +1 -1
- package/locales/sk/LC_MESSAGES/volto.po +45 -0
- package/locales/sk.json +1 -1
- package/locales/sl/LC_MESSAGES/volto.po +45 -0
- package/locales/sl.json +1 -1
- package/locales/sm/LC_MESSAGES/volto.po +45 -0
- package/locales/sm.json +1 -1
- package/locales/sq/LC_MESSAGES/volto.po +45 -0
- package/locales/sq.json +1 -1
- package/locales/sr/LC_MESSAGES/volto.po +45 -0
- package/locales/sr.json +1 -1
- package/locales/sr@cyrl/LC_MESSAGES/volto.po +45 -0
- package/locales/sr@cyrl.json +1 -1
- package/locales/sr@latn/LC_MESSAGES/volto.po +45 -0
- package/locales/sr@latn.json +1 -1
- package/locales/sv/LC_MESSAGES/volto.po +45 -0
- package/locales/sv.json +1 -1
- package/locales/ta/LC_MESSAGES/volto.po +45 -0
- package/locales/ta.json +1 -1
- package/locales/te/LC_MESSAGES/volto.po +45 -0
- package/locales/te.json +1 -1
- package/locales/th/LC_MESSAGES/volto.po +45 -0
- package/locales/th.json +1 -1
- package/locales/to/LC_MESSAGES/volto.po +45 -0
- package/locales/to.json +1 -1
- package/locales/tr/LC_MESSAGES/volto.po +45 -0
- package/locales/tr.json +1 -1
- package/locales/uk/LC_MESSAGES/volto.po +45 -0
- package/locales/uk.json +1 -1
- package/locales/vi/LC_MESSAGES/volto.po +45 -0
- package/locales/vi.json +1 -1
- package/locales/volto.pot +46 -1
- package/locales/zh_CN/LC_MESSAGES/volto.po +45 -0
- package/locales/zh_CN.json +1 -1
- package/locales/zh_Hant/LC_MESSAGES/volto.po +45 -0
- package/locales/zh_Hant.json +1 -1
- package/locales/zh_Hant_HK/LC_MESSAGES/volto.po +45 -0
- package/locales/zh_Hant_HK.json +1 -1
- package/package.json +26 -28
- package/src/components/manage/Blocks/Block/Order/Item.jsx +18 -10
- package/src/components/manage/Blocks/Block/Order/Item.test.jsx +90 -0
- package/src/components/manage/Contents/ContentsBreadcrumbs.jsx +4 -3
- package/src/components/manage/Controlpanels/Users/UserGroupMembershipControlPanel.test.jsx +3 -0
- package/src/components/manage/Toast/Toast.jsx +32 -0
- package/src/components/manage/Toast/Toast.test.jsx +9 -5
- package/src/components/manage/Widgets/UrlWidget.jsx +47 -18
- package/src/components/theme/AlternateHrefLangs/AlternateHrefLangs.jsx +1 -0
- package/src/components/theme/AlternateHrefLangs/AlternateHrefLangs.test.jsx +30 -0
- package/src/components/theme/Sitemap/Sitemap.stories.jsx +82 -0
- package/src/components/theme/Unauthorized/Unauthorized.jsx +30 -23
- package/src/components/theme/Unauthorized/Unauthorized.test.jsx +28 -1
- package/src/components/theme/View/EventView.stories.jsx +89 -0
- package/src/components/theme/View/FileView.stories.jsx +50 -0
- package/src/components/theme/View/LinkView.stories.jsx +57 -0
- package/src/components/theme/View/ListingView.stories.jsx +70 -0
- package/src/components/theme/View/NewsItemView.stories.jsx +58 -0
- package/src/components/theme/View/RenderBlocks.stories.jsx +112 -0
- package/src/components/theme/View/SummaryView.stories.jsx +71 -0
- package/src/components/theme/View/TabularView.stories.jsx +66 -0
- package/src/helpers/FormValidation/validators.ts +15 -2
- package/theme/themes/pastanaga/extras/main.less +1 -0
- package/types/components/manage/Blocks/Block/Order/Item.test.d.ts +1 -0
- package/types/components/theme/Sitemap/Sitemap.stories.d.ts +13 -0
- package/types/components/theme/View/EventView.stories.d.ts +19 -0
- package/types/components/theme/View/FileView.stories.d.ts +18 -0
- package/types/components/theme/View/LinkView.stories.d.ts +18 -0
- package/types/components/theme/View/ListingView.stories.d.ts +24 -0
- package/types/components/theme/View/NewsItemView.stories.d.ts +23 -0
- package/types/components/theme/View/RenderBlocks.stories.d.ts +23 -0
- package/types/components/theme/View/SummaryView.stories.d.ts +23 -0
- package/types/components/theme/View/TabularView.stories.d.ts +23 -0
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
}
|
|
10
10
|
],
|
|
11
11
|
"license": "MIT",
|
|
12
|
-
"version": "19.0.0-alpha.
|
|
12
|
+
"version": "19.0.0-alpha.30",
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
15
|
"url": "git@github.com:plone/volto.git"
|
|
@@ -90,8 +90,8 @@
|
|
|
90
90
|
"@dnd-kit/core": "6.0.8",
|
|
91
91
|
"@dnd-kit/sortable": "7.0.2",
|
|
92
92
|
"@dnd-kit/utilities": "3.2.2",
|
|
93
|
-
"@loadable/component": "5.
|
|
94
|
-
"@loadable/server": "5.
|
|
93
|
+
"@loadable/component": "5.16.7",
|
|
94
|
+
"@loadable/server": "5.16.7",
|
|
95
95
|
"@redux-devtools/extension": "^3.3.0",
|
|
96
96
|
"classnames": "2.5.1",
|
|
97
97
|
"connected-react-router": "6.8.0",
|
|
@@ -115,14 +115,14 @@
|
|
|
115
115
|
"jwt-decode": "2.2.0",
|
|
116
116
|
"linkify-it": "3.0.2",
|
|
117
117
|
"locale": "0.1.0",
|
|
118
|
-
"lodash": "4.
|
|
118
|
+
"lodash": "4.18.1",
|
|
119
119
|
"lodash-move": "1.1.1",
|
|
120
120
|
"moment": "2.29.4",
|
|
121
121
|
"object-assign": "4.1.1",
|
|
122
122
|
"prepend-http": "2",
|
|
123
123
|
"prettier": "3.2.5",
|
|
124
124
|
"pretty-bytes": "5.3.0",
|
|
125
|
-
"prismjs": "1.
|
|
125
|
+
"prismjs": "1.30.0",
|
|
126
126
|
"process": "^0.11.10",
|
|
127
127
|
"promise-file-reader": "1.0.2",
|
|
128
128
|
"prop-types": "15.7.2",
|
|
@@ -180,11 +180,11 @@
|
|
|
180
180
|
"universal-cookie-express": "4.0.3",
|
|
181
181
|
"url": "^0.11.3",
|
|
182
182
|
"use-deep-compare-effect": "1.8.1",
|
|
183
|
-
"uuid": "^
|
|
184
|
-
"@plone/registry": "3.0.0-alpha.
|
|
185
|
-
"@plone/
|
|
186
|
-
"@plone/volto-slate": "19.0.0-alpha.
|
|
187
|
-
"@plone/
|
|
183
|
+
"uuid": "^14.0.0",
|
|
184
|
+
"@plone/registry": "3.0.0-alpha.11",
|
|
185
|
+
"@plone/scripts": "4.0.0-alpha.6",
|
|
186
|
+
"@plone/volto-slate": "19.0.0-alpha.15",
|
|
187
|
+
"@plone/components": "4.0.0-alpha.7"
|
|
188
188
|
},
|
|
189
189
|
"devDependencies": {
|
|
190
190
|
"@babel/core": "^7.28.5",
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
"@babel/runtime": "^7.28.4",
|
|
195
195
|
"@babel/types": "7.20.5",
|
|
196
196
|
"@fiverr/afterbuild-webpack-plugin": "^1.0.0",
|
|
197
|
-
"@loadable/babel-plugin": "5.
|
|
197
|
+
"@loadable/babel-plugin": "5.16.1",
|
|
198
198
|
"@loadable/webpack-plugin": "5.15.2",
|
|
199
199
|
"@sinonjs/fake-timers": "^6.0.1",
|
|
200
200
|
"@storybook/addon-actions": "^8.0.4",
|
|
@@ -208,11 +208,11 @@
|
|
|
208
208
|
"@storybook/test": "^8.0.4",
|
|
209
209
|
"@storybook/theming": "^8.0.4",
|
|
210
210
|
"@testing-library/cypress": "10.1.0",
|
|
211
|
-
"@testing-library/jest-dom": "6.
|
|
211
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
212
212
|
"@testing-library/react": "14.3.1",
|
|
213
213
|
"@testing-library/react-hooks": "8.0.1",
|
|
214
214
|
"@types/history": "^4.7.11",
|
|
215
|
-
"@types/loadable__component": "^5.13.
|
|
215
|
+
"@types/loadable__component": "^5.13.10",
|
|
216
216
|
"@types/lodash": "^4.14.201",
|
|
217
217
|
"@types/node": "^24",
|
|
218
218
|
"@types/react": "^18",
|
|
@@ -221,7 +221,6 @@
|
|
|
221
221
|
"@types/react-router-dom": "^5.3.3",
|
|
222
222
|
"@types/react-test-renderer": "18.0.7",
|
|
223
223
|
"@types/redux-mock-store": "^1.5.0",
|
|
224
|
-
"@types/uuid": "^9.0.2",
|
|
225
224
|
"@typescript-eslint/eslint-plugin": "^7.7.0",
|
|
226
225
|
"@typescript-eslint/parser": "^7.7.0",
|
|
227
226
|
"@vitejs/plugin-react": "^4.3.4",
|
|
@@ -249,7 +248,7 @@
|
|
|
249
248
|
"eslint-plugin-prettier": "^5.1.3",
|
|
250
249
|
"eslint-plugin-react": "^7.34.1",
|
|
251
250
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
252
|
-
"html-webpack-plugin": "5.
|
|
251
|
+
"html-webpack-plugin": "5.6.7",
|
|
253
252
|
"identity-obj-proxy": "3.0.0",
|
|
254
253
|
"jiti": "^2.4.2",
|
|
255
254
|
"jsdom": "^28.1.0",
|
|
@@ -257,19 +256,18 @@
|
|
|
257
256
|
"less": "3.13.1",
|
|
258
257
|
"less-loader": "11.1.0",
|
|
259
258
|
"lodash-webpack-plugin": "0.11.6",
|
|
260
|
-
"mini-css-extract-plugin": "2.
|
|
259
|
+
"mini-css-extract-plugin": "2.10.2",
|
|
261
260
|
"moment-locales-webpack-plugin": "1.2.0",
|
|
262
|
-
"postcss": "8.
|
|
261
|
+
"postcss": "^8.5.10",
|
|
263
262
|
"postcss-flexbugs-fixes": "5.0.2",
|
|
264
263
|
"postcss-less": "6.0.0",
|
|
265
|
-
"postcss-load-config": "
|
|
266
|
-
"postcss-loader": "
|
|
267
|
-
"postcss-
|
|
268
|
-
"
|
|
269
|
-
"react-docgen-typescript-plugin": "^1.0.5",
|
|
264
|
+
"postcss-load-config": "^6.0.1",
|
|
265
|
+
"postcss-loader": "^8.2.1",
|
|
266
|
+
"postcss-scss": "4.0.9",
|
|
267
|
+
"react-docgen-typescript-plugin": "^1.0.8",
|
|
270
268
|
"react-error-overlay": "6.0.9",
|
|
271
269
|
"react-is": "^18.2.0",
|
|
272
|
-
"release-it": "^
|
|
270
|
+
"release-it": "^20.0.1",
|
|
273
271
|
"resolve-url-loader": "^5.0.0",
|
|
274
272
|
"sass": "1.58.0",
|
|
275
273
|
"sass-loader": "^10.0.3",
|
|
@@ -283,7 +281,7 @@
|
|
|
283
281
|
"svg-loader": "0.0.2",
|
|
284
282
|
"svgo": "^3.0.0",
|
|
285
283
|
"svgo-loader": "3.0.3",
|
|
286
|
-
"terser-webpack-plugin": "5.
|
|
284
|
+
"terser-webpack-plugin": "5.4.0",
|
|
287
285
|
"ts-loader": "9.4.4",
|
|
288
286
|
"typescript": "^5.7.3",
|
|
289
287
|
"use-trace-update": "1.3.2",
|
|
@@ -293,11 +291,11 @@
|
|
|
293
291
|
"webpack-bundle-analyzer": "4.10.1",
|
|
294
292
|
"webpack-dev-server": "^5.2.3",
|
|
295
293
|
"webpack-node-externals": "3.0.0",
|
|
296
|
-
"@plone/types": "2.0.0-alpha.16",
|
|
297
294
|
"@plone/babel-preset-razzle": "^1.0.0-alpha.0",
|
|
298
|
-
"@plone/razzle": "1.0.0-alpha.
|
|
299
|
-
"@plone/
|
|
300
|
-
"@plone/
|
|
295
|
+
"@plone/razzle": "1.0.0-alpha.4",
|
|
296
|
+
"@plone/volto-coresandbox": "1.0.0",
|
|
297
|
+
"@plone/types": "2.0.0-alpha.17",
|
|
298
|
+
"@plone/razzle-dev-utils": "1.0.0-alpha.2"
|
|
301
299
|
},
|
|
302
300
|
"scripts": {
|
|
303
301
|
"analyze": "BUNDLE_ANALYZE=true razzle build",
|
|
@@ -44,6 +44,12 @@ export const Item = forwardRef(
|
|
|
44
44
|
config.blocks.blocksConfig[data?.['@type']]?.icon ||
|
|
45
45
|
config.blocks.blocksConfig.title?.icon;
|
|
46
46
|
|
|
47
|
+
const required =
|
|
48
|
+
typeof data?.required === 'boolean'
|
|
49
|
+
? data.required
|
|
50
|
+
: includes(config.blocks.requiredBlocks, data?.['@type']);
|
|
51
|
+
const fixed = !!data?.fixed;
|
|
52
|
+
|
|
47
53
|
return (
|
|
48
54
|
<li
|
|
49
55
|
className={classNames(
|
|
@@ -93,15 +99,17 @@ export const Item = forwardRef(
|
|
|
93
99
|
ref={ref}
|
|
94
100
|
style={style}
|
|
95
101
|
>
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
{!fixed && (
|
|
103
|
+
<button
|
|
104
|
+
ref={ref}
|
|
105
|
+
{...handleProps}
|
|
106
|
+
className={classNames('action', 'drag')}
|
|
107
|
+
tabIndex={0}
|
|
108
|
+
data-cypress="draggable-handle"
|
|
109
|
+
>
|
|
110
|
+
<Icon name={dragSVG} size="16px" />
|
|
111
|
+
</button>
|
|
112
|
+
)}
|
|
105
113
|
<span
|
|
106
114
|
className={cx('text', {
|
|
107
115
|
errored: errors && Object.keys(errors).length > 0,
|
|
@@ -118,7 +126,7 @@ export const Item = forwardRef(
|
|
|
118
126
|
config.blocks.blocksConfig[data?.['@type']]?.title ||
|
|
119
127
|
data?.title}
|
|
120
128
|
</span>
|
|
121
|
-
{!clone && onRemove && (
|
|
129
|
+
{!clone && onRemove && !required && (
|
|
122
130
|
<button
|
|
123
131
|
onClick={onRemove}
|
|
124
132
|
className={classNames('action', 'delete')}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import configureStore from 'redux-mock-store';
|
|
3
|
+
import { Provider } from 'react-intl-redux';
|
|
4
|
+
import { render } from '@testing-library/react';
|
|
5
|
+
import config from '@plone/volto/registry';
|
|
6
|
+
|
|
7
|
+
import { Item } from './Item';
|
|
8
|
+
|
|
9
|
+
const mockStore = configureStore();
|
|
10
|
+
|
|
11
|
+
const defaultStoreState = {
|
|
12
|
+
intl: {
|
|
13
|
+
locale: 'en',
|
|
14
|
+
messages: {},
|
|
15
|
+
},
|
|
16
|
+
form: {
|
|
17
|
+
ui: {
|
|
18
|
+
selected: null,
|
|
19
|
+
hovered: null,
|
|
20
|
+
multiSelected: [],
|
|
21
|
+
gridSelected: null,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const renderItem = (data = {}) => {
|
|
27
|
+
const store = mockStore(defaultStoreState);
|
|
28
|
+
|
|
29
|
+
return render(
|
|
30
|
+
<Provider store={store}>
|
|
31
|
+
<Item
|
|
32
|
+
id="title-block-id"
|
|
33
|
+
data={{ '@type': 'title', ...data }}
|
|
34
|
+
depth={0}
|
|
35
|
+
indentationWidth={25}
|
|
36
|
+
onRemove={() => {}}
|
|
37
|
+
onSelectBlock={() => {}}
|
|
38
|
+
handleProps={{}}
|
|
39
|
+
/>
|
|
40
|
+
</Provider>,
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
describe('Order Item', () => {
|
|
45
|
+
let requiredBlocks;
|
|
46
|
+
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
requiredBlocks = [...(config.blocks.requiredBlocks || [])];
|
|
49
|
+
config.blocks.requiredBlocks = [];
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
afterEach(() => {
|
|
53
|
+
config.blocks.requiredBlocks = requiredBlocks;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('renders drag and delete actions for movable and removable blocks', () => {
|
|
57
|
+
const { container } = renderItem();
|
|
58
|
+
|
|
59
|
+
expect(container.querySelector('.action.drag')).not.toBeNull();
|
|
60
|
+
expect(container.querySelector('.action.delete')).not.toBeNull();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('hides delete action for required blocks', () => {
|
|
64
|
+
const { container } = renderItem({ required: true });
|
|
65
|
+
|
|
66
|
+
expect(container.querySelector('.action.delete')).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('hides delete action for block types configured as required', () => {
|
|
70
|
+
config.blocks.requiredBlocks = ['title'];
|
|
71
|
+
|
|
72
|
+
const { container } = renderItem();
|
|
73
|
+
|
|
74
|
+
expect(container.querySelector('.action.delete')).toBeNull();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('allows explicit required=false to override required block types', () => {
|
|
78
|
+
config.blocks.requiredBlocks = ['title'];
|
|
79
|
+
|
|
80
|
+
const { container } = renderItem({ required: false });
|
|
81
|
+
|
|
82
|
+
expect(container.querySelector('.action.delete')).not.toBeNull();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('hides drag action for fixed blocks', () => {
|
|
86
|
+
const { container } = renderItem({ fixed: true });
|
|
87
|
+
|
|
88
|
+
expect(container.querySelector('.action.drag')).toBeNull();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Breadcrumb } from 'semantic-ui-react';
|
|
3
|
+
import UniversalLink from '@plone/volto/components/manage/UniversalLink/UniversalLink';
|
|
3
4
|
import { Link } from 'react-router-dom';
|
|
4
5
|
import { defineMessages, useIntl } from 'react-intl';
|
|
5
6
|
import { useSelector } from 'react-redux';
|
|
@@ -43,13 +44,13 @@ const ContentsBreadcrumbs = (props) => {
|
|
|
43
44
|
<ContentsBreadcrumbsRootItem />
|
|
44
45
|
</Link>
|
|
45
46
|
<Breadcrumb.Divider />
|
|
46
|
-
<
|
|
47
|
-
|
|
47
|
+
<UniversalLink
|
|
48
|
+
href={`${navroot?.['@id']}/contents`}
|
|
48
49
|
className="section"
|
|
49
50
|
title={navroot?.title}
|
|
50
51
|
>
|
|
51
52
|
{navroot?.title}
|
|
52
|
-
</
|
|
53
|
+
</UniversalLink>
|
|
53
54
|
</>
|
|
54
55
|
)}
|
|
55
56
|
{items.map((breadcrumb, index, breadcrumbs) => [
|
|
@@ -14,6 +14,9 @@ vi.mock('../../Toolbar/Toolbar', () => ({
|
|
|
14
14
|
describe('UserGroupMembershipControlPanel', () => {
|
|
15
15
|
it('renders a user group membership control component', () => {
|
|
16
16
|
const store = mockStore({
|
|
17
|
+
userSession: {
|
|
18
|
+
token: '1234',
|
|
19
|
+
},
|
|
17
20
|
controlpanels: {
|
|
18
21
|
controlpanel: {
|
|
19
22
|
data: {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
+
import { defineMessages, useIntl } from 'react-intl';
|
|
3
4
|
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
4
5
|
|
|
5
6
|
import successSVG from '@plone/volto/icons/ready.svg';
|
|
@@ -7,7 +8,28 @@ import infoSVG from '@plone/volto/icons/info.svg';
|
|
|
7
8
|
import errorSVG from '@plone/volto/icons/error.svg';
|
|
8
9
|
import warningSVG from '@plone/volto/icons/warning.svg';
|
|
9
10
|
|
|
11
|
+
const messages = defineMessages({
|
|
12
|
+
success: {
|
|
13
|
+
id: 'toast_type_success',
|
|
14
|
+
defaultMessage: 'Success',
|
|
15
|
+
},
|
|
16
|
+
error: {
|
|
17
|
+
id: 'toast_type_error',
|
|
18
|
+
defaultMessage: 'Error',
|
|
19
|
+
},
|
|
20
|
+
warning: {
|
|
21
|
+
id: 'toast_type_warning',
|
|
22
|
+
defaultMessage: 'Warning',
|
|
23
|
+
},
|
|
24
|
+
info: {
|
|
25
|
+
id: 'toast_type_info',
|
|
26
|
+
defaultMessage: 'Information',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
10
30
|
const Toast = (props) => {
|
|
31
|
+
const intl = useIntl();
|
|
32
|
+
|
|
11
33
|
function getIcon(props) {
|
|
12
34
|
if (props.info) {
|
|
13
35
|
return infoSVG;
|
|
@@ -22,12 +44,22 @@ const Toast = (props) => {
|
|
|
22
44
|
}
|
|
23
45
|
}
|
|
24
46
|
|
|
47
|
+
function getTypeLabel(props) {
|
|
48
|
+
if (props.error) return intl.formatMessage(messages.error);
|
|
49
|
+
if (props.warning) return intl.formatMessage(messages.warning);
|
|
50
|
+
if (props.info) return intl.formatMessage(messages.info);
|
|
51
|
+
if (props.success) return intl.formatMessage(messages.success);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
25
55
|
const { title, content } = props;
|
|
56
|
+
const typeLabel = getTypeLabel(props);
|
|
26
57
|
|
|
27
58
|
return (
|
|
28
59
|
<>
|
|
29
60
|
<Icon name={getIcon(props)} size="18px" />
|
|
30
61
|
<div className="toast-inner-content">
|
|
62
|
+
{typeLabel && <span className="visually-hidden">{typeLabel}</span>}
|
|
31
63
|
{title && <h4>{title}</h4>}
|
|
32
64
|
<div>{content}</div>
|
|
33
65
|
</div>
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import renderer from 'react-test-renderer';
|
|
3
|
+
import { IntlProvider } from 'react-intl';
|
|
3
4
|
import Toast from './Toast';
|
|
4
5
|
|
|
5
6
|
test('renders a Toast info component', () => {
|
|
6
7
|
const component = renderer.create(
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
<IntlProvider locale="en">
|
|
9
|
+
<Toast
|
|
10
|
+
info
|
|
11
|
+
title="I'm a title"
|
|
12
|
+
content="This is the content, lorem ipsum"
|
|
13
|
+
/>
|
|
14
|
+
,
|
|
15
|
+
</IntlProvider>,
|
|
12
16
|
);
|
|
13
17
|
const json = component.toJSON();
|
|
14
18
|
expect(json).toMatchSnapshot();
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* UrlWidget component.
|
|
3
3
|
* @module components/manage/Widgets/UrlWidget
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
5
|
import React, { useState } from 'react';
|
|
7
6
|
import PropTypes from 'prop-types';
|
|
8
7
|
import { Input, Button } from 'semantic-ui-react';
|
|
@@ -14,10 +13,10 @@ import {
|
|
|
14
13
|
flattenToAppURL,
|
|
15
14
|
URLUtils,
|
|
16
15
|
} from '@plone/volto/helpers/Url/Url';
|
|
16
|
+
import { defineMessages, useIntl } from 'react-intl';
|
|
17
17
|
import withObjectBrowser from '@plone/volto/components/manage/Sidebar/ObjectBrowser';
|
|
18
18
|
import clearSVG from '@plone/volto/icons/clear.svg';
|
|
19
19
|
import navTreeSVG from '@plone/volto/icons/nav.svg';
|
|
20
|
-
|
|
21
20
|
/** Widget to edit urls
|
|
22
21
|
*
|
|
23
22
|
* This is the default widget used for the `remoteUrl` field. You can also use
|
|
@@ -30,6 +29,24 @@ import navTreeSVG from '@plone/volto/icons/nav.svg';
|
|
|
30
29
|
* }
|
|
31
30
|
* ```
|
|
32
31
|
*/
|
|
32
|
+
const messages = defineMessages({
|
|
33
|
+
urlMissing: {
|
|
34
|
+
id: 'URL is missing',
|
|
35
|
+
defaultMessage: 'URL is missing',
|
|
36
|
+
},
|
|
37
|
+
urlInvalid: {
|
|
38
|
+
id: 'URL is invalid',
|
|
39
|
+
defaultMessage: 'URL is invalid',
|
|
40
|
+
},
|
|
41
|
+
clearUrl: {
|
|
42
|
+
id: 'Clear URL',
|
|
43
|
+
defaultMessage: 'Clear URL',
|
|
44
|
+
},
|
|
45
|
+
openUrlBrowser: {
|
|
46
|
+
id: 'Open URL browser',
|
|
47
|
+
defaultMessage: 'Open URL browser',
|
|
48
|
+
},
|
|
49
|
+
});
|
|
33
50
|
export const UrlWidget = (props) => {
|
|
34
51
|
const {
|
|
35
52
|
id,
|
|
@@ -40,9 +57,10 @@ export const UrlWidget = (props) => {
|
|
|
40
57
|
maxLength,
|
|
41
58
|
placeholder,
|
|
42
59
|
isDisabled,
|
|
60
|
+
required,
|
|
43
61
|
} = props;
|
|
44
62
|
const inputId = `field-${id}`;
|
|
45
|
-
|
|
63
|
+
const intl = useIntl();
|
|
46
64
|
const [value, setValue] = useState(flattenToAppURL(props.value));
|
|
47
65
|
const [isInvalid, setIsInvalid] = useState(false);
|
|
48
66
|
/**
|
|
@@ -54,24 +72,20 @@ export const UrlWidget = (props) => {
|
|
|
54
72
|
const clear = () => {
|
|
55
73
|
setValue('');
|
|
56
74
|
onChange(id, undefined);
|
|
75
|
+
setIsInvalid(false);
|
|
57
76
|
};
|
|
58
|
-
|
|
59
77
|
const onChangeValue = (_value) => {
|
|
60
78
|
let newValue = _value;
|
|
61
79
|
if (newValue?.length > 0) {
|
|
62
80
|
if (isInvalid && URLUtils.isUrl(URLUtils.normalizeUrl(newValue))) {
|
|
63
81
|
setIsInvalid(false);
|
|
64
82
|
}
|
|
65
|
-
|
|
66
83
|
if (isInternalURL(newValue)) {
|
|
67
84
|
newValue = flattenToAppURL(newValue);
|
|
68
85
|
}
|
|
69
86
|
}
|
|
70
|
-
|
|
71
87
|
setValue(newValue);
|
|
72
|
-
|
|
73
88
|
newValue = isInternalURL(newValue) ? addAppURL(newValue) : newValue;
|
|
74
|
-
|
|
75
89
|
if (!isInternalURL(newValue) && newValue.length > 0) {
|
|
76
90
|
const checkedURL = URLUtils.checkAndNormalizeUrl(newValue);
|
|
77
91
|
newValue = checkedURL.url;
|
|
@@ -79,10 +93,15 @@ export const UrlWidget = (props) => {
|
|
|
79
93
|
setIsInvalid(true);
|
|
80
94
|
}
|
|
81
95
|
}
|
|
82
|
-
|
|
83
96
|
onChange(id, newValue === '' ? undefined : newValue);
|
|
84
97
|
};
|
|
85
|
-
|
|
98
|
+
// A11y: if the field is required and the user leaves it empty, we mark it as missing
|
|
99
|
+
const handleBlur = ({ target }) => {
|
|
100
|
+
if (required && (!target.value || target.value === '')) {
|
|
101
|
+
setIsInvalid(true);
|
|
102
|
+
}
|
|
103
|
+
onBlur(id, target.value === '' ? undefined : target.value);
|
|
104
|
+
};
|
|
86
105
|
return (
|
|
87
106
|
<FormFieldWrapper {...props} className="url wide">
|
|
88
107
|
<div className="wrapper">
|
|
@@ -90,25 +109,38 @@ export const UrlWidget = (props) => {
|
|
|
90
109
|
id={inputId}
|
|
91
110
|
name={id}
|
|
92
111
|
type="url"
|
|
112
|
+
required={required}
|
|
113
|
+
aria-required={required}
|
|
114
|
+
aria-invalid={isInvalid}
|
|
115
|
+
aria-errormessage={isInvalid ? `${inputId}-error` : undefined}
|
|
116
|
+
onBlur={handleBlur}
|
|
93
117
|
value={value || ''}
|
|
94
118
|
disabled={isDisabled}
|
|
95
119
|
placeholder={placeholder}
|
|
96
120
|
onChange={({ target }) => onChangeValue(target.value)}
|
|
97
|
-
onBlur={({ target }) =>
|
|
98
|
-
onBlur(id, target.value === '' ? undefined : target.value)
|
|
99
|
-
}
|
|
100
121
|
onClick={() => onClick()}
|
|
101
122
|
minLength={minLength || null}
|
|
102
123
|
maxLength={maxLength || null}
|
|
103
124
|
error={isInvalid}
|
|
104
125
|
/>
|
|
126
|
+
{isInvalid && (
|
|
127
|
+
<span
|
|
128
|
+
id={`${inputId}-error`}
|
|
129
|
+
role="alert"
|
|
130
|
+
className="visually-hidden"
|
|
131
|
+
>
|
|
132
|
+
{value?.length > 0
|
|
133
|
+
? intl.formatMessage(messages.urlInvalid)
|
|
134
|
+
: intl.formatMessage(messages.urlMissing)}
|
|
135
|
+
</span>
|
|
136
|
+
)}
|
|
105
137
|
{value?.length > 0 ? (
|
|
106
138
|
<Button.Group>
|
|
107
139
|
<Button
|
|
108
140
|
type="button"
|
|
109
141
|
basic
|
|
110
142
|
className="cancel"
|
|
111
|
-
aria-label=
|
|
143
|
+
aria-label={intl.formatMessage(messages.clearUrl)}
|
|
112
144
|
onClick={(e) => {
|
|
113
145
|
e.preventDefault();
|
|
114
146
|
e.stopPropagation();
|
|
@@ -124,7 +156,7 @@ export const UrlWidget = (props) => {
|
|
|
124
156
|
type="button"
|
|
125
157
|
basic
|
|
126
158
|
icon
|
|
127
|
-
aria-label=
|
|
159
|
+
aria-label={intl.formatMessage(messages.openUrlBrowser)}
|
|
128
160
|
onClick={(e) => {
|
|
129
161
|
e.preventDefault();
|
|
130
162
|
e.stopPropagation();
|
|
@@ -145,7 +177,6 @@ export const UrlWidget = (props) => {
|
|
|
145
177
|
</FormFieldWrapper>
|
|
146
178
|
);
|
|
147
179
|
};
|
|
148
|
-
|
|
149
180
|
/**
|
|
150
181
|
* Property types
|
|
151
182
|
* @property {Object} propTypes Property types.
|
|
@@ -166,7 +197,6 @@ UrlWidget.propTypes = {
|
|
|
166
197
|
openObjectBrowser: PropTypes.func.isRequired,
|
|
167
198
|
placeholder: PropTypes.string,
|
|
168
199
|
};
|
|
169
|
-
|
|
170
200
|
/**
|
|
171
201
|
* Default properties.
|
|
172
202
|
* @property {Object} defaultProps Default properties.
|
|
@@ -183,5 +213,4 @@ UrlWidget.defaultProps = {
|
|
|
183
213
|
minLength: null,
|
|
184
214
|
maxLength: null,
|
|
185
215
|
};
|
|
186
|
-
|
|
187
216
|
export default withObjectBrowser(UrlWidget);
|
|
@@ -35,6 +35,36 @@ describe('AlternateHrefLangs', () => {
|
|
|
35
35
|
expect(helmetLinks.length).toBe(0);
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
+
it('multilingual site, content without language field, renders nothing', () => {
|
|
39
|
+
config.settings.publicURL = 'https://plone.org';
|
|
40
|
+
config.settings.supportedLanguages = ['en', 'es'];
|
|
41
|
+
|
|
42
|
+
const content = {
|
|
43
|
+
'@id': 'http://localhost:8080/Plone/en/newsroom/news',
|
|
44
|
+
'@components': {
|
|
45
|
+
translations: {
|
|
46
|
+
items: [{ '@id': 'http://localhost:8080/Plone/es', language: 'es' }],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const store = mockStore({
|
|
52
|
+
intl: {
|
|
53
|
+
locale: 'en',
|
|
54
|
+
messages: {},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
renderer.create(
|
|
59
|
+
<Provider store={store}>
|
|
60
|
+
<AlternateHrefLangs content={content} />
|
|
61
|
+
</Provider>,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const helmetLinks = Helmet.peek().linkTags;
|
|
65
|
+
expect(helmetLinks.length).toBe(0);
|
|
66
|
+
});
|
|
67
|
+
|
|
38
68
|
it('multilingual site, with some translations', () => {
|
|
39
69
|
config.settings.publicURL = 'https://plone.org';
|
|
40
70
|
config.settings.supportedLanguages = ['en', 'es', 'eu'];
|