@plone/volto 18.35.0 → 18.36.0
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 +58 -0
- package/locales/af/LC_MESSAGES/volto.po +42 -11
- package/locales/af.json +1 -1
- package/locales/ar/LC_MESSAGES/volto.po +42 -11
- package/locales/ar.json +1 -1
- package/locales/bg/LC_MESSAGES/volto.po +42 -11
- package/locales/bg.json +1 -1
- package/locales/bn/LC_MESSAGES/volto.po +42 -11
- package/locales/bn.json +1 -1
- package/locales/ca/LC_MESSAGES/volto.po +42 -11
- package/locales/ca.json +1 -1
- package/locales/cs/LC_MESSAGES/volto.po +42 -11
- package/locales/cs.json +1 -1
- package/locales/cy/LC_MESSAGES/volto.po +42 -11
- package/locales/cy.json +1 -1
- package/locales/da/LC_MESSAGES/volto.po +42 -11
- package/locales/da.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +42 -11
- package/locales/de.json +1 -1
- package/locales/el/LC_MESSAGES/volto.po +42 -11
- package/locales/el.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +42 -11
- package/locales/en.json +1 -1
- package/locales/en_AU/LC_MESSAGES/volto.po +42 -11
- package/locales/en_AU.json +1 -1
- package/locales/en_GB/LC_MESSAGES/volto.po +42 -11
- package/locales/en_GB.json +1 -1
- package/locales/eo/LC_MESSAGES/volto.po +42 -11
- package/locales/eo.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +42 -11
- package/locales/es.json +1 -1
- package/locales/et/LC_MESSAGES/volto.po +42 -11
- package/locales/et.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +42 -11
- package/locales/eu.json +1 -1
- package/locales/fa/LC_MESSAGES/volto.po +42 -11
- package/locales/fa.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +42 -11
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +42 -11
- package/locales/fr.json +1 -1
- package/locales/fu/LC_MESSAGES/volto.po +42 -11
- package/locales/fu.json +1 -1
- package/locales/gl/LC_MESSAGES/volto.po +42 -11
- package/locales/gl.json +1 -1
- package/locales/he/LC_MESSAGES/volto.po +42 -11
- package/locales/he.json +1 -1
- package/locales/hi/LC_MESSAGES/volto.po +42 -11
- package/locales/hi.json +1 -1
- package/locales/hr/LC_MESSAGES/volto.po +42 -11
- package/locales/hr.json +1 -1
- package/locales/hu/LC_MESSAGES/volto.po +42 -11
- package/locales/hu.json +1 -1
- package/locales/hy/LC_MESSAGES/volto.po +42 -11
- package/locales/hy.json +1 -1
- package/locales/id/LC_MESSAGES/volto.po +42 -11
- package/locales/id.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +42 -11
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +42 -11
- package/locales/ja.json +1 -1
- package/locales/ka/LC_MESSAGES/volto.po +42 -11
- package/locales/ka.json +1 -1
- package/locales/kn/LC_MESSAGES/volto.po +42 -11
- package/locales/kn.json +1 -1
- package/locales/ko/LC_MESSAGES/volto.po +42 -11
- package/locales/ko.json +1 -1
- package/locales/lt/LC_MESSAGES/volto.po +42 -11
- package/locales/lt.json +1 -1
- package/locales/lv/LC_MESSAGES/volto.po +42 -11
- package/locales/lv.json +1 -1
- package/locales/mi/LC_MESSAGES/volto.po +42 -11
- package/locales/mi.json +1 -1
- package/locales/mk/LC_MESSAGES/volto.po +42 -11
- package/locales/mk.json +1 -1
- package/locales/my/LC_MESSAGES/volto.po +42 -11
- package/locales/my.json +1 -1
- package/locales/nb_NO/LC_MESSAGES/volto.po +42 -11
- package/locales/nb_NO.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +42 -11
- package/locales/nl.json +1 -1
- package/locales/nn/LC_MESSAGES/volto.po +42 -11
- package/locales/nn.json +1 -1
- package/locales/pl/LC_MESSAGES/volto.po +42 -11
- package/locales/pl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +42 -11
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +42 -11
- package/locales/pt_BR.json +1 -1
- package/locales/rm/LC_MESSAGES/volto.po +42 -11
- package/locales/rm.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +42 -11
- package/locales/ro.json +1 -1
- package/locales/ru/LC_MESSAGES/volto.po +42 -11
- package/locales/ru.json +1 -1
- package/locales/sk/LC_MESSAGES/volto.po +42 -11
- package/locales/sk.json +1 -1
- package/locales/sl/LC_MESSAGES/volto.po +42 -11
- package/locales/sl.json +1 -1
- package/locales/sm/LC_MESSAGES/volto.po +42 -11
- package/locales/sm.json +1 -1
- package/locales/sq/LC_MESSAGES/volto.po +42 -11
- package/locales/sq.json +1 -1
- package/locales/sr/LC_MESSAGES/volto.po +42 -11
- package/locales/sr.json +1 -1
- package/locales/sr@cyrl/LC_MESSAGES/volto.po +42 -11
- package/locales/sr@cyrl.json +1 -1
- package/locales/sr@latn/LC_MESSAGES/volto.po +42 -11
- package/locales/sr@latn.json +1 -1
- package/locales/sv/LC_MESSAGES/volto.po +42 -11
- package/locales/sv.json +1 -1
- package/locales/ta/LC_MESSAGES/volto.po +42 -11
- package/locales/ta.json +1 -1
- package/locales/te/LC_MESSAGES/volto.po +42 -11
- package/locales/te.json +1 -1
- package/locales/th/LC_MESSAGES/volto.po +42 -11
- package/locales/th.json +1 -1
- package/locales/to/LC_MESSAGES/volto.po +42 -11
- package/locales/to.json +1 -1
- package/locales/tr/LC_MESSAGES/volto.po +42 -11
- package/locales/tr.json +1 -1
- package/locales/uk/LC_MESSAGES/volto.po +42 -11
- package/locales/uk.json +1 -1
- package/locales/vi/LC_MESSAGES/volto.po +42 -11
- package/locales/vi.json +1 -1
- package/locales/volto.pot +40 -9
- package/locales/zh_CN/LC_MESSAGES/volto.po +42 -11
- package/locales/zh_CN.json +1 -1
- package/locales/zh_Hant/LC_MESSAGES/volto.po +42 -11
- package/locales/zh_Hant.json +1 -1
- package/locales/zh_Hant_HK/LC_MESSAGES/volto.po +42 -11
- package/locales/zh_Hant_HK.json +1 -1
- package/package.json +18 -22
- package/src/actions/querystringsearch/querystringsearch.js +4 -1
- package/src/actions/querystringsearch/querystringsearch.test.js +77 -0
- package/src/components/manage/BlockChooser/BlockChooser.jsx +7 -10
- package/src/components/manage/Blocks/Block/Edit.jsx +9 -10
- package/src/components/manage/Blocks/Block/Order/Item.jsx +9 -4
- package/src/components/manage/Blocks/Image/View.test.jsx +8 -8
- package/src/components/manage/Contents/Contents.jsx +3 -3
- package/src/components/manage/Contents/ContentsBreadcrumbs.jsx +7 -6
- package/src/components/manage/Contents/ContentsItem.jsx +1 -3
- package/src/components/manage/Contents/DropZoneContent.jsx +1 -0
- package/src/components/manage/Sidebar/ObjectBrowser.jsx +7 -0
- package/src/components/manage/Sidebar/ObjectBrowserBody.jsx +30 -5
- package/src/components/manage/Sidebar/ObjectBrowserBody.test.jsx +52 -0
- package/src/components/manage/Toolbar/More.jsx +13 -0
- package/src/components/manage/Toolbar/Toolbar.jsx +91 -8
- package/src/components/manage/Widgets/ArrayWidget.jsx +3 -2
- package/src/components/manage/Widgets/FileWidget.jsx +71 -17
- package/src/components/manage/Widgets/FormFieldWrapper.jsx +16 -3
- package/src/components/manage/Widgets/ObjectBrowserWidget.jsx +1 -0
- package/src/components/manage/Widgets/QuerystringWidget.jsx +1 -18
- package/src/components/manage/Widgets/QuerystringWidget.test.jsx +45 -2
- package/src/components/manage/Widgets/RegistryImageWidget.jsx +4 -2
- package/src/components/manage/Widgets/SelectStyling.jsx +33 -1
- package/src/components/manage/Widgets/SelectWidget.jsx +3 -2
- package/src/components/manage/Widgets/TextWidget.test.jsx +44 -0
- package/src/components/theme/Sitemap/Sitemap.jsx +21 -28
- package/src/components/theme/Sitemap/Sitemap.test.jsx +18 -0
- package/src/components/theme/Unauthorized/Unauthorized.jsx +1 -2
- 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/config/index.js +1 -0
- package/src/helpers/I18n/I18n.test.ts +44 -0
- package/src/helpers/I18n/I18n.ts +31 -0
- package/src/helpers/index.js +1 -0
- package/src/server.jsx +7 -1
- package/theme/themes/pastanaga/extras/focus.less +33 -0
- package/theme/themes/pastanaga/extras/main.less +2 -0
- package/theme/themes/pastanaga/extras/skiplinks.less +0 -2
- package/theme/themes/pastanaga/globals/site.variables +7 -0
- package/types/components/manage/Controlpanels/Relations/RelationsMatrix.d.ts +1 -1
- package/types/components/manage/Multilingual/ManageTranslations.d.ts +1 -1
- package/types/components/manage/Sidebar/ObjectBrowser.d.ts +1 -1
- package/types/components/manage/Sidebar/ObjectBrowserBody.test.d.ts +1 -0
- package/types/components/manage/Widgets/ImageWidget.d.ts +1 -1
- package/types/components/manage/Widgets/InternalUrlWidget.d.ts +1 -1
- package/types/components/manage/Widgets/QuerystringWidget.d.ts +0 -4
- package/types/components/manage/Widgets/UrlWidget.d.ts +1 -1
- package/types/components/manage/Widgets/index.d.ts +2 -2
- 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/types/helpers/I18n/I18n.d.ts +20 -0
- package/types/helpers/index.d.ts +1 -0
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
}
|
|
10
10
|
],
|
|
11
11
|
"license": "MIT",
|
|
12
|
-
"version": "18.
|
|
12
|
+
"version": "18.36.0",
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
15
|
"url": "git@github.com:plone/volto.git"
|
|
@@ -154,7 +154,6 @@
|
|
|
154
154
|
"diff": "3.5.1",
|
|
155
155
|
"express": "4.22.1",
|
|
156
156
|
"filesize": "6",
|
|
157
|
-
"full-icu": "1.4.0",
|
|
158
157
|
"github-slugger": "1.4.0",
|
|
159
158
|
"history": "4.10.1",
|
|
160
159
|
"hoist-non-react-statics": "3.3.2",
|
|
@@ -173,7 +172,7 @@
|
|
|
173
172
|
"prepend-http": "2",
|
|
174
173
|
"pretty-bytes": "5.3.0",
|
|
175
174
|
"prettier": "3.2.5",
|
|
176
|
-
"prismjs": "1.
|
|
175
|
+
"prismjs": "1.30.0",
|
|
177
176
|
"process": "^0.11.10",
|
|
178
177
|
"promise-file-reader": "1.0.2",
|
|
179
178
|
"prop-types": "15.7.2",
|
|
@@ -207,7 +206,6 @@
|
|
|
207
206
|
"react-side-effect": "2.1.2",
|
|
208
207
|
"react-simple-code-editor": "0.7.1",
|
|
209
208
|
"react-sortable-hoc": "2.0.0",
|
|
210
|
-
"react-test-renderer": "18.2.0",
|
|
211
209
|
"react-toastify": "5.5.0",
|
|
212
210
|
"react-transition-group": "4.4.5",
|
|
213
211
|
"react-virtualized": "9.22.3",
|
|
@@ -215,7 +213,6 @@
|
|
|
215
213
|
"redux-actions": "3.0.0",
|
|
216
214
|
"redux-connect": "10.0.0",
|
|
217
215
|
"redux-localstorage-simple": "2.5.1",
|
|
218
|
-
"redux-mock-store": "1.5.4",
|
|
219
216
|
"redux-thunk": "2.4.2",
|
|
220
217
|
"rrule": "2.7.1",
|
|
221
218
|
"semantic-ui-less": "2.4.1",
|
|
@@ -231,10 +228,10 @@
|
|
|
231
228
|
"universal-cookie-express": "4.0.3",
|
|
232
229
|
"url": "^0.11.3",
|
|
233
230
|
"use-deep-compare-effect": "1.8.1",
|
|
234
|
-
"uuid": "^
|
|
235
|
-
"@plone/scripts": "3.10.
|
|
236
|
-
"@plone/registry": "2.7.
|
|
237
|
-
"@plone/volto-slate": "18.10.
|
|
231
|
+
"uuid": "^14.0.0",
|
|
232
|
+
"@plone/scripts": "3.10.7",
|
|
233
|
+
"@plone/registry": "2.7.3",
|
|
234
|
+
"@plone/volto-slate": "18.10.2"
|
|
238
235
|
},
|
|
239
236
|
"devDependencies": {
|
|
240
237
|
"@babel/core": "^7.0.0",
|
|
@@ -263,8 +260,8 @@
|
|
|
263
260
|
"@storybook/test": "^8.0.4",
|
|
264
261
|
"@storybook/theming": "^8.0.4",
|
|
265
262
|
"@testing-library/cypress": "10.1.0",
|
|
266
|
-
"@testing-library/jest-dom": "6.
|
|
267
|
-
"@testing-library/react": "
|
|
263
|
+
"@testing-library/jest-dom": "6.9.1",
|
|
264
|
+
"@testing-library/react": "^16.3.2",
|
|
268
265
|
"@testing-library/react-hooks": "8.0.1",
|
|
269
266
|
"@types/history": "^4.7.11",
|
|
270
267
|
"@types/jest": "^29.5.8",
|
|
@@ -277,11 +274,10 @@
|
|
|
277
274
|
"@types/react-router-dom": "^5.3.3",
|
|
278
275
|
"@types/react-test-renderer": "18.0.7",
|
|
279
276
|
"@types/redux-mock-store": "^1.5.0",
|
|
280
|
-
"@types/uuid": "^9.0.2",
|
|
281
277
|
"@typescript-eslint/eslint-plugin": "^7.7.0",
|
|
282
278
|
"@typescript-eslint/parser": "^7.7.0",
|
|
283
279
|
"@vitejs/plugin-react": "^4.3.4",
|
|
284
|
-
"@vitest/ui": "^2.
|
|
280
|
+
"@vitest/ui": "^3.2.4",
|
|
285
281
|
"autoprefixer": "10.4.8",
|
|
286
282
|
"axe-core": "4.4.2",
|
|
287
283
|
"babel-loader": "9.1.0",
|
|
@@ -321,20 +317,20 @@
|
|
|
321
317
|
"lodash-webpack-plugin": "0.11.6",
|
|
322
318
|
"mini-css-extract-plugin": "2.10.2",
|
|
323
319
|
"moment-locales-webpack-plugin": "1.2.0",
|
|
324
|
-
"postcss": "8.
|
|
320
|
+
"postcss": "^8.5.15",
|
|
325
321
|
"postcss-flexbugs-fixes": "5.0.2",
|
|
326
322
|
"postcss-less": "6.0.0",
|
|
327
|
-
"postcss-load-config": "
|
|
328
|
-
"postcss-loader": "
|
|
329
|
-
"postcss-
|
|
330
|
-
"postcss-scss": "4.0.6",
|
|
323
|
+
"postcss-load-config": "^6.0.1",
|
|
324
|
+
"postcss-loader": "^8.2.1",
|
|
325
|
+
"postcss-scss": "4.0.9",
|
|
331
326
|
"razzle": "4.2.18",
|
|
332
327
|
"razzle-dev-utils": "4.2.18",
|
|
333
328
|
"razzle-plugin-scss": "4.2.18",
|
|
334
|
-
"react-docgen-typescript-plugin": "^1.0.8",
|
|
335
329
|
"react-error-overlay": "6.0.9",
|
|
336
330
|
"react-is": "^18.2.0",
|
|
337
|
-
"
|
|
331
|
+
"react-test-renderer": "18.2.0",
|
|
332
|
+
"redux-mock-store": "1.5.4",
|
|
333
|
+
"release-it": "^20.0.1",
|
|
338
334
|
"semver": "^7.5.4",
|
|
339
335
|
"start-server-and-test": "2.1.5",
|
|
340
336
|
"storybook": "^8.6.15",
|
|
@@ -357,7 +353,7 @@
|
|
|
357
353
|
"webpack-dev-server": "4.11.1",
|
|
358
354
|
"webpack-node-externals": "3.0.0",
|
|
359
355
|
"why": "0.6.2",
|
|
360
|
-
"@plone/types": "1.6.
|
|
356
|
+
"@plone/types": "1.6.3",
|
|
361
357
|
"@plone/volto-coresandbox": "1.0.0"
|
|
362
358
|
},
|
|
363
359
|
"scripts": {
|
|
@@ -369,7 +365,7 @@
|
|
|
369
365
|
"vitest": "vitest",
|
|
370
366
|
"coverage": "vitest run --coverage",
|
|
371
367
|
"test": "razzle test --maxWorkers=${MAX_WORKERS:-50%}",
|
|
372
|
-
"test:ci": "CI=true
|
|
368
|
+
"test:ci": "CI=true razzle test",
|
|
373
369
|
"test:husky": "CI=true yarn test --bail --findRelatedTests",
|
|
374
370
|
"start:prod": "NODE_ENV=production node build/server.js",
|
|
375
371
|
"prettier": "prettier --single-quote --check '{src,cypress}/**/*.{js,jsx,ts,tsx}' --check '*.js'",
|
|
@@ -17,7 +17,10 @@ export function getQueryStringResults(path, data, subrequest, page) {
|
|
|
17
17
|
delete requestData.depth;
|
|
18
18
|
requestData.query.forEach((q) => {
|
|
19
19
|
if (q.i === 'path') {
|
|
20
|
-
|
|
20
|
+
// fixes https://github.com/plone/volto/issues/8349
|
|
21
|
+
// Skip if depth is already embedded in the path value (e.g. set via object browser)
|
|
22
|
+
const hasEmbeddedDepth = /::\d+$/.test(q.v);
|
|
23
|
+
if (!hasEmbeddedDepth) q.v += '::' + data.depth;
|
|
21
24
|
}
|
|
22
25
|
});
|
|
23
26
|
}
|
|
@@ -73,4 +73,81 @@ describe('querystringsearch action', () => {
|
|
|
73
73
|
expect(action.request.path).toEqual('/@querystring-search');
|
|
74
74
|
expect(action.request.data.sort_order).toEqual('descending');
|
|
75
75
|
});
|
|
76
|
+
|
|
77
|
+
describe('depth handling', () => {
|
|
78
|
+
it('appends depth to path value when depth is set and path has no embedded depth', () => {
|
|
79
|
+
const data = {
|
|
80
|
+
query: [
|
|
81
|
+
{
|
|
82
|
+
i: 'path',
|
|
83
|
+
o: 'plone.app.querystring.operation.string.path',
|
|
84
|
+
v: '/folder',
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
depth: 2,
|
|
88
|
+
};
|
|
89
|
+
const action = getQueryStringResults('', data);
|
|
90
|
+
|
|
91
|
+
const pathQuery = action.request.data.query.find((q) => q.i === 'path');
|
|
92
|
+
expect(pathQuery.v).toEqual('/folder::2');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('does not double-append depth when path value already contains an embedded depth', () => {
|
|
96
|
+
const data = {
|
|
97
|
+
query: [
|
|
98
|
+
{
|
|
99
|
+
i: 'path',
|
|
100
|
+
o: 'plone.app.querystring.operation.string.path',
|
|
101
|
+
v: '/folder::1',
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
depth: 3,
|
|
105
|
+
};
|
|
106
|
+
const action = getQueryStringResults('', data);
|
|
107
|
+
|
|
108
|
+
const pathQuery = action.request.data.query.find((q) => q.i === 'path');
|
|
109
|
+
expect(pathQuery.v).toEqual('/folder::1');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('removes depth from the top-level request data', () => {
|
|
113
|
+
const data = {
|
|
114
|
+
query: [
|
|
115
|
+
{
|
|
116
|
+
i: 'path',
|
|
117
|
+
o: 'plone.app.querystring.operation.string.path',
|
|
118
|
+
v: '/folder',
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
depth: 2,
|
|
122
|
+
};
|
|
123
|
+
const action = getQueryStringResults('', data);
|
|
124
|
+
|
|
125
|
+
expect(action.request.data.depth).toBeUndefined();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('handles mixed path entries: appends depth only to those without embedded depth', () => {
|
|
129
|
+
const data = {
|
|
130
|
+
query: [
|
|
131
|
+
{
|
|
132
|
+
i: 'path',
|
|
133
|
+
o: 'plone.app.querystring.operation.string.path',
|
|
134
|
+
v: '/folder-a',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
i: 'path',
|
|
138
|
+
o: 'plone.app.querystring.operation.string.path',
|
|
139
|
+
v: '/folder-b::5',
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
depth: 2,
|
|
143
|
+
};
|
|
144
|
+
const action = getQueryStringResults('', data);
|
|
145
|
+
|
|
146
|
+
const [pathA, pathB] = action.request.data.query.filter(
|
|
147
|
+
(q) => q.i === 'path',
|
|
148
|
+
);
|
|
149
|
+
expect(pathA.v).toEqual('/folder-a::2');
|
|
150
|
+
expect(pathB.v).toEqual('/folder-b::5');
|
|
151
|
+
});
|
|
152
|
+
});
|
|
76
153
|
});
|
|
@@ -10,6 +10,7 @@ import { useIntl, defineMessages } from 'react-intl';
|
|
|
10
10
|
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
11
11
|
import AnimateHeight from 'react-animate-height';
|
|
12
12
|
import config from '@plone/volto/registry';
|
|
13
|
+
import { formatMessageWithFallback } from '@plone/volto/helpers/I18n/I18n';
|
|
13
14
|
import upSVG from '@plone/volto/icons/up-key.svg';
|
|
14
15
|
import downSVG from '@plone/volto/icons/down-key.svg';
|
|
15
16
|
import BlockChooserSearch from './BlockChooserSearch';
|
|
@@ -98,16 +99,10 @@ const BlockChooser = ({
|
|
|
98
99
|
}
|
|
99
100
|
const [filterValue, setFilterValue] = React.useState('');
|
|
100
101
|
|
|
101
|
-
const getFormatMessage = (message) =>
|
|
102
|
-
intl.formatMessage({
|
|
103
|
-
id: message,
|
|
104
|
-
defaultMessage: message,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
102
|
function blocksAvailableFilter(blocks) {
|
|
108
103
|
return blocks.filter(
|
|
109
104
|
(block) =>
|
|
110
|
-
|
|
105
|
+
formatMessageWithFallback(intl, block.title)
|
|
111
106
|
.toLowerCase()
|
|
112
107
|
.includes(filterValue.toLowerCase()) ||
|
|
113
108
|
block.title.toLowerCase().includes(filterValue.toLowerCase()) ||
|
|
@@ -117,7 +112,7 @@ const BlockChooser = ({
|
|
|
117
112
|
function filterVariations(block) {
|
|
118
113
|
return block.variations?.filter(
|
|
119
114
|
(variation) =>
|
|
120
|
-
|
|
115
|
+
formatMessageWithFallback(intl, variation.title)
|
|
121
116
|
.toLowerCase()
|
|
122
117
|
.includes(filterValue.toLowerCase()) &&
|
|
123
118
|
!variation.title.toLowerCase().includes('default'),
|
|
@@ -145,9 +140,11 @@ const BlockChooser = ({
|
|
|
145
140
|
}}
|
|
146
141
|
>
|
|
147
142
|
<Icon name={block.icon} size="36px" />
|
|
148
|
-
{
|
|
143
|
+
{formatMessageWithFallback(intl, block.title)}
|
|
149
144
|
{filterValue && variations?.[0]?.title && (
|
|
150
|
-
<small>
|
|
145
|
+
<small>
|
|
146
|
+
{formatMessageWithFallback(intl, variations[0].title)}
|
|
147
|
+
</small>
|
|
151
148
|
)}
|
|
152
149
|
</Button>
|
|
153
150
|
</Button.Group>
|
|
@@ -149,15 +149,6 @@ export class Edit extends Component {
|
|
|
149
149
|
this.props.setUIState({ hovered: this.props.id });
|
|
150
150
|
}
|
|
151
151
|
}}
|
|
152
|
-
onFocus={(e) => {
|
|
153
|
-
// TODO: This `onFocus` steals somehow the focus from the slate block
|
|
154
|
-
// we have to investigate why this is happening
|
|
155
|
-
// Apparently, I can't see any difference in the behavior
|
|
156
|
-
// If any, we can fix it in successive iterations
|
|
157
|
-
// if (this.props.hovered !== this.props.id) {
|
|
158
|
-
// this.props.setUIState({ hovered: this.props.id });
|
|
159
|
-
// }
|
|
160
|
-
}}
|
|
161
152
|
onMouseLeave={(e) => {
|
|
162
153
|
e.preventDefault();
|
|
163
154
|
e.stopPropagation();
|
|
@@ -172,6 +163,15 @@ export class Edit extends Component {
|
|
|
172
163
|
e,
|
|
173
164
|
);
|
|
174
165
|
}}
|
|
166
|
+
onFocus={(e) => {
|
|
167
|
+
const isMultipleSelection = e.shiftKey || e.ctrlKey || e.metaKey;
|
|
168
|
+
!this.props.selected &&
|
|
169
|
+
this.props.onSelectBlock(
|
|
170
|
+
this.props.id,
|
|
171
|
+
this.props.selected ? false : isMultipleSelection,
|
|
172
|
+
e,
|
|
173
|
+
);
|
|
174
|
+
}}
|
|
175
175
|
onKeyDown={
|
|
176
176
|
!(blockHasOwnFocusManagement || disableNewBlocks)
|
|
177
177
|
? (e) =>
|
|
@@ -245,7 +245,6 @@ export class Edit extends Component {
|
|
|
245
245
|
className={cx(`block ${type}`, { selected: this.props.selected })}
|
|
246
246
|
style={{ outline: 'none' }}
|
|
247
247
|
ref={this.blockNode}
|
|
248
|
-
// The tabIndex is required for the keyboard navigation
|
|
249
248
|
tabIndex={-1}
|
|
250
249
|
>
|
|
251
250
|
{this.props.intl.formatMessage(messages.unknownBlock, {
|
|
@@ -5,7 +5,9 @@ import includes from 'lodash/includes';
|
|
|
5
5
|
import cx from 'classnames';
|
|
6
6
|
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
7
7
|
import { setUIState } from '@plone/volto/actions/form/form';
|
|
8
|
+
import { formatMessageWithFallback } from '@plone/volto/helpers/I18n/I18n';
|
|
8
9
|
import config from '@plone/volto/registry';
|
|
10
|
+
import { useIntl } from 'react-intl';
|
|
9
11
|
|
|
10
12
|
import deleteSVG from '@plone/volto/icons/delete.svg';
|
|
11
13
|
import dragSVG from '@plone/volto/icons/drag.svg';
|
|
@@ -34,6 +36,7 @@ export const Item = forwardRef(
|
|
|
34
36
|
},
|
|
35
37
|
ref,
|
|
36
38
|
) => {
|
|
39
|
+
const intl = useIntl();
|
|
37
40
|
const selected = useSelector((state) => state.form.ui.selected);
|
|
38
41
|
const hovered = useSelector((state) => state.form.ui.hovered);
|
|
39
42
|
const multiSelected = useSelector((state) => state.form.ui.multiSelected);
|
|
@@ -49,7 +52,11 @@ export const Item = forwardRef(
|
|
|
49
52
|
? data.required
|
|
50
53
|
: includes(config.blocks.requiredBlocks, data?.['@type']);
|
|
51
54
|
const fixed = !!data?.fixed;
|
|
52
|
-
|
|
55
|
+
const configTitle = config.blocks.blocksConfig[data?.['@type']]?.title;
|
|
56
|
+
const blockTitle =
|
|
57
|
+
data?.plaintext ||
|
|
58
|
+
formatMessageWithFallback(intl, configTitle) ||
|
|
59
|
+
data?.title;
|
|
53
60
|
return (
|
|
54
61
|
<li
|
|
55
62
|
className={classNames(
|
|
@@ -122,9 +129,7 @@ export const Item = forwardRef(
|
|
|
122
129
|
style={{ verticalAlign: 'middle' }}
|
|
123
130
|
/>
|
|
124
131
|
)}{' '}
|
|
125
|
-
{
|
|
126
|
-
config.blocks.blocksConfig[data?.['@type']]?.title ||
|
|
127
|
-
data?.title}
|
|
132
|
+
{blockTitle}
|
|
128
133
|
</span>
|
|
129
134
|
{!clone && onRemove && !required && (
|
|
130
135
|
<button
|
|
@@ -36,13 +36,13 @@ config.blocks.blocksConfig = {
|
|
|
36
36
|
|
|
37
37
|
describe('Image View Component', () => {
|
|
38
38
|
test('renders a view image component with a local image', () => {
|
|
39
|
-
const {
|
|
40
|
-
const img =
|
|
39
|
+
const { container } = render(<View data={{ url: '/image.jpg' }} />);
|
|
40
|
+
const img = container.querySelector('img');
|
|
41
41
|
expect(img).toHaveAttribute('src', '/image.jpg/@@images/image');
|
|
42
42
|
expect(img).toHaveAttribute('loading', 'lazy');
|
|
43
43
|
});
|
|
44
44
|
test('renders a view image component with a local image with a link', () => {
|
|
45
|
-
const { container
|
|
45
|
+
const { container } = render(
|
|
46
46
|
<Provider store={store}>
|
|
47
47
|
<MemoryRouter>
|
|
48
48
|
<View
|
|
@@ -51,22 +51,22 @@ describe('Image View Component', () => {
|
|
|
51
51
|
</MemoryRouter>
|
|
52
52
|
</Provider>,
|
|
53
53
|
);
|
|
54
|
-
const img =
|
|
54
|
+
const img = container.querySelector('img');
|
|
55
55
|
const a = container.querySelector('a');
|
|
56
56
|
expect(img).toHaveAttribute('src', '/image.jpg/@@images/image');
|
|
57
57
|
expect(a).toHaveAttribute('href', '/front-page');
|
|
58
58
|
});
|
|
59
59
|
test('renders a view image component with an external image', () => {
|
|
60
|
-
const {
|
|
60
|
+
const { container } = render(
|
|
61
61
|
<Provider store={store}>
|
|
62
62
|
<View data={{ url: 'https://plone.org/logo.jpg' }} />
|
|
63
63
|
</Provider>,
|
|
64
64
|
);
|
|
65
|
-
const img =
|
|
65
|
+
const img = container.querySelector('img');
|
|
66
66
|
expect(img).toHaveAttribute('src', 'https://plone.org/logo.jpg');
|
|
67
67
|
});
|
|
68
68
|
test('renders a view image component with an external image with a link', () => {
|
|
69
|
-
const { container
|
|
69
|
+
const { container } = render(
|
|
70
70
|
<Provider store={store}>
|
|
71
71
|
<View
|
|
72
72
|
data={{
|
|
@@ -76,7 +76,7 @@ describe('Image View Component', () => {
|
|
|
76
76
|
/>
|
|
77
77
|
</Provider>,
|
|
78
78
|
);
|
|
79
|
-
const img =
|
|
79
|
+
const img = container.querySelector('img');
|
|
80
80
|
const a = container.querySelector('a');
|
|
81
81
|
expect(img).toHaveAttribute('src', 'https://plone.org/logo.jpg');
|
|
82
82
|
expect(a).toHaveAttribute('href', 'http://front-page');
|
|
@@ -724,9 +724,9 @@ class Contents extends Component {
|
|
|
724
724
|
delta,
|
|
725
725
|
);
|
|
726
726
|
} else {
|
|
727
|
-
this.setState({
|
|
728
|
-
items: move(
|
|
729
|
-
});
|
|
727
|
+
this.setState((prevState) => ({
|
|
728
|
+
items: move(prevState.items, itemIndex, itemIndex + delta),
|
|
729
|
+
}));
|
|
730
730
|
}
|
|
731
731
|
}
|
|
732
732
|
|
|
@@ -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, useLocation } from 'react-router-dom';
|
|
4
5
|
import { defineMessages, useIntl } from 'react-intl';
|
|
5
6
|
import langmap from '@plone/volto/helpers/LanguageMap/LanguageMap';
|
|
@@ -41,22 +42,22 @@ const ContentsBreadcrumbs = (props) => {
|
|
|
41
42
|
</>
|
|
42
43
|
)}
|
|
43
44
|
{settings.isMultilingual && pathname?.split('/')?.length > 2 && (
|
|
44
|
-
<
|
|
45
|
-
|
|
45
|
+
<UniversalLink
|
|
46
|
+
href={`/${lang}/contents`}
|
|
46
47
|
className="section"
|
|
47
48
|
title={intl.formatMessage(messages.home)}
|
|
48
49
|
>
|
|
49
50
|
{langmap?.[lang]?.nativeName ?? lang}
|
|
50
|
-
</
|
|
51
|
+
</UniversalLink>
|
|
51
52
|
)}
|
|
52
53
|
{!settings.isMultilingual && (
|
|
53
|
-
<
|
|
54
|
-
|
|
54
|
+
<UniversalLink
|
|
55
|
+
href="/contents"
|
|
55
56
|
className="section"
|
|
56
57
|
title={intl.formatMessage(messages.home)}
|
|
57
58
|
>
|
|
58
59
|
<ContentsBreadcrumbsHomeItem />
|
|
59
|
-
</
|
|
60
|
+
</UniversalLink>
|
|
60
61
|
)}
|
|
61
62
|
{items.map((breadcrumb, index, breadcrumbs) => [
|
|
62
63
|
<Breadcrumb.Divider key={`divider-${breadcrumb.url}`} />,
|
|
@@ -364,15 +364,13 @@ const DragDropConnector = (props) => {
|
|
|
364
364
|
drop(props, monitor) {
|
|
365
365
|
const id = monitor.getItem().id;
|
|
366
366
|
const dragOrder = monitor.getItem().startOrder;
|
|
367
|
-
const dropOrder =
|
|
367
|
+
const dropOrder = monitor.getItem().order;
|
|
368
368
|
|
|
369
369
|
if (dragOrder === dropOrder) {
|
|
370
370
|
return;
|
|
371
371
|
}
|
|
372
372
|
|
|
373
373
|
props.onOrderItem(id, dragOrder, dropOrder - dragOrder, true);
|
|
374
|
-
|
|
375
|
-
monitor.getItem().order = dropOrder;
|
|
376
374
|
},
|
|
377
375
|
},
|
|
378
376
|
(connect) => ({
|
|
@@ -58,6 +58,7 @@ const withObjectBrowser = (WrappedComponent) =>
|
|
|
58
58
|
selectableTypes,
|
|
59
59
|
maximumSelectionSize,
|
|
60
60
|
currentPath,
|
|
61
|
+
initialPath,
|
|
61
62
|
onlyFolderishSelectable,
|
|
62
63
|
} = {}) =>
|
|
63
64
|
this.setState(() => ({
|
|
@@ -71,6 +72,7 @@ const withObjectBrowser = (WrappedComponent) =>
|
|
|
71
72
|
selectableTypes,
|
|
72
73
|
maximumSelectionSize,
|
|
73
74
|
currentPath,
|
|
75
|
+
initialPath,
|
|
74
76
|
onlyFolderishSelectable,
|
|
75
77
|
}));
|
|
76
78
|
|
|
@@ -82,6 +84,10 @@ const withObjectBrowser = (WrappedComponent) =>
|
|
|
82
84
|
this.props.pathname ||
|
|
83
85
|
this.props.location?.pathname;
|
|
84
86
|
|
|
87
|
+
let initialPath = this.state?.initialPath
|
|
88
|
+
? getBaseUrl(this.state.initialPath)
|
|
89
|
+
: null;
|
|
90
|
+
|
|
85
91
|
return (
|
|
86
92
|
<>
|
|
87
93
|
<WrappedComponent
|
|
@@ -105,6 +111,7 @@ const withObjectBrowser = (WrappedComponent) =>
|
|
|
105
111
|
: this.props.data
|
|
106
112
|
}
|
|
107
113
|
contextURL={getBaseUrl(contextURL)}
|
|
114
|
+
initialPath={initialPath}
|
|
108
115
|
closeObjectBrowser={this.closeObjectBrowser}
|
|
109
116
|
mode={this.state.mode}
|
|
110
117
|
onSelectItem={this.state.onSelectItem}
|
|
@@ -5,6 +5,7 @@ import { connect } from 'react-redux';
|
|
|
5
5
|
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
|
6
6
|
import { Input, Segment, Breadcrumb } from 'semantic-ui-react';
|
|
7
7
|
|
|
8
|
+
import debounce from 'lodash/debounce';
|
|
8
9
|
import join from 'lodash/join';
|
|
9
10
|
|
|
10
11
|
// These absolute imports (without using the corresponding centralized index.js) are required
|
|
@@ -84,6 +85,7 @@ class ObjectBrowserBody extends Component {
|
|
|
84
85
|
onSelectItem: PropTypes.func,
|
|
85
86
|
dataName: PropTypes.string,
|
|
86
87
|
maximumSelectionSize: PropTypes.number,
|
|
88
|
+
initialPath: PropTypes.string,
|
|
87
89
|
contextURL: PropTypes.string,
|
|
88
90
|
searchableTypes: PropTypes.arrayOf(PropTypes.string),
|
|
89
91
|
onlyFolderishSelectable: PropTypes.bool,
|
|
@@ -113,18 +115,21 @@ class ObjectBrowserBody extends Component {
|
|
|
113
115
|
*/
|
|
114
116
|
constructor(props) {
|
|
115
117
|
super(props);
|
|
118
|
+
const defaultMultiplePath = props.initialPath || '/';
|
|
116
119
|
this.state = {
|
|
117
120
|
currentFolder:
|
|
118
|
-
this.props.mode === 'multiple'
|
|
121
|
+
this.props.mode === 'multiple'
|
|
122
|
+
? defaultMultiplePath
|
|
123
|
+
: this.props.contextURL || '/',
|
|
119
124
|
currentImageFolder:
|
|
120
125
|
this.props.mode === 'multiple'
|
|
121
|
-
?
|
|
126
|
+
? defaultMultiplePath
|
|
122
127
|
: this.props.mode === 'image' && this.props.data?.url
|
|
123
128
|
? getParentURL(this.props.data.url)
|
|
124
129
|
: '/',
|
|
125
130
|
currentLinkFolder:
|
|
126
131
|
this.props.mode === 'multiple'
|
|
127
|
-
?
|
|
132
|
+
? defaultMultiplePath
|
|
128
133
|
: this.props.mode === 'link' && this.props.data?.href
|
|
129
134
|
? getParentURL(this.props.data.href)
|
|
130
135
|
: '/',
|
|
@@ -155,6 +160,22 @@ class ObjectBrowserBody extends Component {
|
|
|
155
160
|
view: this.props.mode === 'image' ? 'icons' : 'list',
|
|
156
161
|
};
|
|
157
162
|
this.searchInputRef = React.createRef();
|
|
163
|
+
// Debounce the live-search dispatch so rapid keystrokes do not produce a
|
|
164
|
+
// burst of overlapping requests whose responses may resolve out of order
|
|
165
|
+
// (an older, less-filtered response arriving after the latest one would
|
|
166
|
+
// visually "reset" the list to look unfiltered).
|
|
167
|
+
this.debouncedSearch = debounce(this.doSearch, 300);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Component will unmount
|
|
172
|
+
* @method componentWillUnmount
|
|
173
|
+
* @returns {undefined}
|
|
174
|
+
*/
|
|
175
|
+
componentWillUnmount() {
|
|
176
|
+
if (this.debouncedSearch) {
|
|
177
|
+
this.debouncedSearch.cancel();
|
|
178
|
+
}
|
|
158
179
|
}
|
|
159
180
|
|
|
160
181
|
/**
|
|
@@ -244,8 +265,7 @@ class ObjectBrowserBody extends Component {
|
|
|
244
265
|
view: prevState.view === 'icons' ? 'list' : 'icons',
|
|
245
266
|
}));
|
|
246
267
|
|
|
247
|
-
|
|
248
|
-
const text = flattenToAppURL(e.target.value);
|
|
268
|
+
doSearch = (text) => {
|
|
249
269
|
if (text.startsWith('/')) {
|
|
250
270
|
this.setState({ currentFolder: text });
|
|
251
271
|
this.props.searchContent(
|
|
@@ -282,6 +302,11 @@ class ObjectBrowserBody extends Component {
|
|
|
282
302
|
}
|
|
283
303
|
};
|
|
284
304
|
|
|
305
|
+
onSearch = (e) => {
|
|
306
|
+
const text = flattenToAppURL(e.target.value);
|
|
307
|
+
this.debouncedSearch(text);
|
|
308
|
+
};
|
|
309
|
+
|
|
285
310
|
onSelectItem = (item) => {
|
|
286
311
|
const url = item['@id'];
|
|
287
312
|
const { block, data, mode, dataName, onChangeBlock } = this.props;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import configureStore from 'redux-mock-store';
|
|
4
|
+
import { Provider } from 'react-intl-redux';
|
|
5
|
+
import ObjectBrowserBody from './ObjectBrowserBody';
|
|
6
|
+
|
|
7
|
+
const mockStore = configureStore();
|
|
8
|
+
|
|
9
|
+
const baseState = {
|
|
10
|
+
search: { subrequests: {} },
|
|
11
|
+
intl: { locale: 'en', messages: {} },
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const baseProps = {
|
|
15
|
+
block: 'test-block',
|
|
16
|
+
data: {},
|
|
17
|
+
closeObjectBrowser: () => {},
|
|
18
|
+
onChangeBlock: () => {},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const getInitialSearchPath = (actions) => {
|
|
22
|
+
const action = actions.find((a) => a.type === 'SEARCH_CONTENT');
|
|
23
|
+
return action?.request?.path?.split('/@search')[0];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
describe('ObjectBrowserBody', () => {
|
|
27
|
+
it('uses initialPath as the default folder when mode=multiple', () => {
|
|
28
|
+
const store = mockStore(baseState);
|
|
29
|
+
render(
|
|
30
|
+
<Provider store={store}>
|
|
31
|
+
<ObjectBrowserBody
|
|
32
|
+
{...baseProps}
|
|
33
|
+
mode="multiple"
|
|
34
|
+
initialPath="/company/team"
|
|
35
|
+
/>
|
|
36
|
+
</Provider>,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
expect(getInitialSearchPath(store.getActions())).toBe('/company/team');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('defaults to root when mode=multiple and initialPath is not provided', () => {
|
|
43
|
+
const store = mockStore(baseState);
|
|
44
|
+
render(
|
|
45
|
+
<Provider store={store}>
|
|
46
|
+
<ObjectBrowserBody {...baseProps} mode="multiple" />
|
|
47
|
+
</Provider>,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(getInitialSearchPath(store.getActions())).toBe('/');
|
|
51
|
+
});
|
|
52
|
+
});
|