@plone/volto 17.0.0-alpha.1 → 17.0.0-alpha.11
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/.yarn/install-state.gz +0 -0
- package/CHANGELOG.md +403 -16
- package/CONTRIBUTING.md +1 -1
- package/README.md +12 -15
- package/addon-registry.js +34 -0
- package/create-theme-addons-loader.js +79 -0
- package/cypress/support/commands.js +45 -0
- package/locales/ca/LC_MESSAGES/volto.po +187 -6
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +206 -25
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +186 -5
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +187 -6
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +187 -6
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +4792 -0
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +187 -6
- package/locales/fr.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +187 -6
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +187 -6
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +842 -649
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +187 -6
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +195 -14
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +187 -6
- package/locales/ro.json +1 -1
- package/locales/volto.pot +187 -6
- package/locales/zh_CN/LC_MESSAGES/volto.po +187 -6
- package/locales/zh_CN.json +1 -1
- package/package-why.json +0 -1
- package/package.json +9 -8
- package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +1 -1
- package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +1 -1
- package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +1 -1
- package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +1 -1
- package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +1 -1
- package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +1 -1
- package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +1 -1
- package/packages/volto-slate/build/messages/src/elementEditor/messages.json +1 -1
- package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +1 -1
- package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +1 -1
- package/packages/volto-slate/package.json +1 -1
- package/packages/volto-slate/src/blocks/Table/index.js +2 -0
- package/packages/volto-slate/src/blocks/Text/SlashMenu.jsx +4 -3
- package/packages/volto-slate/src/editor/deserialize.js +0 -1
- package/packages/volto-slate/src/editor/plugins/StyleMenu/StyleMenu.jsx +14 -4
- package/razzle.config.js +28 -0
- package/src/actions/index.js +6 -0
- package/src/actions/language/language.js +9 -8
- package/src/actions/querystringsearch/querystringsearch.js +20 -14
- package/src/actions/relations/rebuild.js +25 -0
- package/src/actions/relations/relations.js +86 -0
- package/src/actions/relations/relations.test.js +15 -0
- package/src/components/index.js +1 -0
- package/src/components/manage/Add/Add.jsx +2 -2
- package/src/components/manage/BlockChooser/BlockChooser.jsx +14 -5
- package/src/components/manage/BlockChooser/BlockChooser.test.jsx +5 -0
- package/src/components/manage/Blocks/Listing/Edit.jsx +0 -19
- package/src/components/manage/Blocks/Listing/ListingBody.jsx +77 -61
- package/src/components/manage/Blocks/Listing/ListingBody.test.jsx +20 -0
- package/src/components/manage/Blocks/Listing/View.jsx +0 -4
- package/src/components/manage/Blocks/Listing/getAsyncData.js +10 -2
- package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +25 -16
- package/src/components/manage/Blocks/Search/SearchBlockEdit.jsx +5 -4
- package/src/components/manage/Blocks/Search/SearchBlockView.jsx +2 -1
- package/src/components/manage/Blocks/Search/components/DateRangeFacet.jsx +4 -1
- package/src/components/manage/Blocks/Search/components/Facets.jsx +58 -2
- package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +32 -15
- package/src/components/manage/Blocks/Search/layout/LeftColumnFacets.jsx +17 -5
- package/src/components/manage/Blocks/Search/layout/RightColumnFacets.jsx +17 -5
- package/src/components/manage/Blocks/Search/layout/TopSideFacets.jsx +21 -5
- package/src/components/manage/Blocks/Search/schema.js +16 -1
- package/src/components/manage/Blocks/ToC/Edit.jsx +1 -0
- package/src/components/manage/Contents/Contents.jsx +69 -33
- package/src/components/manage/Contents/ContentsItem.jsx +6 -0
- package/src/components/manage/Controlpanels/AddonsControlpanel.jsx +3 -3
- package/src/components/manage/Controlpanels/Controlpanels.jsx +199 -224
- package/src/components/manage/Controlpanels/Controlpanels.test.jsx +46 -7
- package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +66 -0
- package/src/components/manage/Controlpanels/Relations/Relations.jsx +114 -0
- package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +479 -0
- package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +531 -0
- package/src/components/manage/Controlpanels/Users/UserGroupMembershipControlPanel.jsx +3 -3
- package/src/components/manage/Controlpanels/Users/UserGroupMembershipListing.jsx +51 -82
- package/src/components/manage/Controlpanels/Users/UserGroupMembershipMatrix.jsx +79 -75
- package/src/components/manage/DragDropList/DragDropList.jsx +63 -42
- package/src/components/manage/Form/BlocksToolbar.jsx +5 -1
- package/src/components/manage/Form/Form.jsx +11 -5
- package/src/components/manage/Form/InlineForm.jsx +39 -9
- package/src/components/manage/Form/InlineFormState.js +8 -0
- package/src/components/manage/Multilingual/CreateTranslation.jsx +2 -2
- package/src/components/manage/Multilingual/TranslationObject.jsx +4 -3
- package/src/components/manage/Preferences/ChangePassword.jsx +2 -2
- package/src/components/manage/Preferences/PersonalPreferences.jsx +2 -2
- package/src/components/manage/Toast/Toast.jsx +1 -1
- package/src/components/manage/Toolbar/Types.jsx +2 -2
- package/src/components/manage/Widgets/DatetimeWidget.jsx +9 -5
- package/src/components/manage/Widgets/ObjectListWidget.jsx +3 -8
- package/src/components/manage/Widgets/RecurrenceWidget/ByDayField.jsx +2 -1
- package/src/components/manage/Widgets/RecurrenceWidget/MonthOfTheYearField.jsx +2 -1
- package/src/components/manage/Widgets/RecurrenceWidget/Occurences.jsx +2 -1
- package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +7 -2
- package/src/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthField.jsx +2 -1
- package/src/components/manage/Widgets/SelectUtils.js +1 -1
- package/src/components/manage/Widgets/SelectWidget.jsx +1 -1
- package/src/components/theme/Footer/Footer.jsx +2 -13
- package/src/components/theme/Header/Header.jsx +37 -63
- package/src/components/theme/Header/Header.test.jsx +18 -0
- package/src/components/theme/Icon/Icon.jsx +2 -2
- package/src/components/theme/LanguageSelector/LanguageSelector.js +8 -3
- package/src/components/theme/Login/Login.jsx +1 -0
- package/src/components/theme/Logo/Logo.jsx +2 -1
- package/src/components/theme/MultilingualRedirector/MultilingualRedirector.jsx +2 -2
- package/src/components/theme/Navigation/NavItem.jsx +4 -2
- package/src/components/theme/NotFound/NotFound.jsx +55 -41
- package/src/components/theme/PasswordReset/PasswordReset.jsx +7 -4
- package/src/components/theme/PasswordReset/RequestPasswordReset.jsx +1 -1
- package/src/components/theme/Sitemap/Sitemap.jsx +5 -3
- package/src/components/theme/View/DefaultView.jsx +1 -1
- package/src/components/theme/View/EventDatesInfo.jsx +2 -1
- package/src/components/theme/View/RenderBlocks.jsx +7 -1
- package/src/components/theme/Widgets/DateWidget.jsx +2 -1
- package/src/components/theme/Widgets/DatetimeWidget.jsx +2 -1
- package/src/components/theme/Widgets/RelationsWidget.jsx +13 -11
- package/src/config/ControlPanels.js +2 -0
- package/src/config/Widgets.jsx +1 -0
- package/src/config/index.js +2 -0
- package/src/constants/ActionTypes.js +4 -0
- package/src/constants/Languages.js +8 -4
- package/src/express-middleware/sitemap.js +36 -4
- package/src/helpers/Api/Api.js +1 -1
- package/src/helpers/FormValidation/FormValidation.js +11 -2
- package/src/helpers/FormValidation/FormValidation.test.js +73 -0
- package/src/helpers/Html/Html.jsx +3 -1
- package/src/helpers/Html/Html.test.jsx +5 -0
- package/src/helpers/MessageLabels/MessageLabels.js +72 -0
- package/src/helpers/Robots/Robots.js +24 -6
- package/src/helpers/Sitemap/Sitemap.js +44 -2
- package/src/helpers/Url/Url.js +27 -6
- package/src/helpers/Url/Url.test.js +26 -0
- package/src/helpers/Utils/Utils.js +38 -13
- package/src/helpers/Utils/Utils.test.js +4 -4
- package/src/helpers/Utils/usePagination.js +72 -14
- package/src/helpers/Utils/usePagination.test.js +115 -0
- package/src/helpers/index.js +7 -2
- package/src/hooks/userSession/useToken.js +5 -0
- package/src/middleware/Api.test.js +54 -0
- package/src/middleware/api.js +8 -4
- package/src/reducers/actions/actions.js +1 -1
- package/src/reducers/breadcrumbs/breadcrumbs.js +1 -1
- package/src/reducers/index.js +2 -0
- package/src/reducers/navigation/navigation.js +1 -1
- package/src/reducers/relations/relations.js +173 -0
- package/src/reducers/types/types.js +1 -1
- package/src/routes.js +5 -0
- package/src/server.jsx +28 -23
- package/test-setup-config.js +1 -0
- package/theme/themes/pastanaga/extras/contents.less +1 -0
- package/theme/themes/pastanaga/extras/main.less +80 -1
- package/theme/themes/pastanaga/extras/search.less +6 -0
- package/theme/themes/pastanaga/extras/sidebar.less +4 -0
- package/theme/themes/pastanaga/extras/userscontrolpanel.less +99 -76
- package/.changelog.draft +0 -28
- package/.editorconfig +0 -36
- package/.storybook/main.js +0 -127
- package/.storybook/manager.js +0 -15
- package/.storybook/preview.js +0 -21
- package/.storybook/static/previewImage.svg +0 -48
- package/.yarnrc.yml +0 -5
- package/jsdoc.json +0 -16
- package/netlify.toml +0 -5
- package/pyvenv.cfg +0 -3
- package/share/man/man1/ttx.1 +0 -225
- package/src/components/theme/Header/Header.md +0 -27
- package/towncrier.toml +0 -33
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
2
|
import { useDispatch, useSelector } from 'react-redux';
|
|
3
3
|
import hoistNonReactStatics from 'hoist-non-react-statics';
|
|
4
4
|
import useDeepCompareEffect from 'use-deep-compare-effect';
|
|
@@ -14,18 +14,23 @@ function getDisplayName(WrappedComponent) {
|
|
|
14
14
|
|
|
15
15
|
export default function withQuerystringResults(WrappedComponent) {
|
|
16
16
|
function WithQuerystringResults(props) {
|
|
17
|
-
const {
|
|
17
|
+
const {
|
|
18
|
+
data = {},
|
|
19
|
+
id = data.block,
|
|
20
|
+
properties: content,
|
|
21
|
+
path,
|
|
22
|
+
variation,
|
|
23
|
+
} = props;
|
|
18
24
|
const { settings } = config;
|
|
19
25
|
const querystring = data.querystring || data; // For backwards compat with data saved before Blocks schema. Note, this is also how the Search block passes data to ListingBody
|
|
20
26
|
|
|
21
|
-
const { block } = data;
|
|
22
27
|
const { b_size = settings.defaultPageSize } = querystring; // batchsize
|
|
23
28
|
|
|
24
29
|
// save the path so it won't trigger dispatch on eager router location change
|
|
25
30
|
const [initialPath] = React.useState(getBaseUrl(path));
|
|
26
31
|
|
|
27
32
|
const copyFields = ['limit', 'query', 'sort_on', 'sort_order', 'depth'];
|
|
28
|
-
|
|
33
|
+
const { currentPage, setCurrentPage } = usePagination(id, 1);
|
|
29
34
|
const adaptedQuery = Object.assign(
|
|
30
35
|
variation?.fullobjects ? { fullobjects: 1 } : { metadata_fields: '_all' },
|
|
31
36
|
{
|
|
@@ -37,7 +42,9 @@ export default function withQuerystringResults(WrappedComponent) {
|
|
|
37
42
|
: {},
|
|
38
43
|
),
|
|
39
44
|
);
|
|
40
|
-
const
|
|
45
|
+
const adaptedQueryRef = useRef(adaptedQuery);
|
|
46
|
+
const currentPageRef = useRef(currentPage);
|
|
47
|
+
|
|
41
48
|
const querystringResults = useSelector(
|
|
42
49
|
(state) => state.querystringsearch.subrequests,
|
|
43
50
|
);
|
|
@@ -45,32 +52,32 @@ export default function withQuerystringResults(WrappedComponent) {
|
|
|
45
52
|
|
|
46
53
|
const folderItems = content?.is_folderish ? content.items : [];
|
|
47
54
|
const hasQuery = querystring?.query?.length > 0;
|
|
48
|
-
const hasLoaded = hasQuery ?
|
|
55
|
+
const hasLoaded = hasQuery ? querystringResults?.[id]?.loaded : true;
|
|
49
56
|
|
|
50
57
|
const listingItems =
|
|
51
|
-
querystring?.query?.length > 0 && querystringResults?.[
|
|
52
|
-
? querystringResults?.[
|
|
58
|
+
querystring?.query?.length > 0 && querystringResults?.[id]
|
|
59
|
+
? querystringResults?.[id]?.items || []
|
|
53
60
|
: folderItems;
|
|
54
61
|
|
|
55
62
|
const showAsFolderListing = !hasQuery && content?.items_total > b_size;
|
|
56
63
|
const showAsQueryListing =
|
|
57
|
-
hasQuery && querystringResults?.[
|
|
64
|
+
hasQuery && querystringResults?.[id]?.total > b_size;
|
|
58
65
|
|
|
59
66
|
const totalPages = showAsFolderListing
|
|
60
67
|
? Math.ceil(content.items_total / b_size)
|
|
61
68
|
: showAsQueryListing
|
|
62
|
-
? Math.ceil(querystringResults[
|
|
69
|
+
? Math.ceil(querystringResults[id].total / b_size)
|
|
63
70
|
: 0;
|
|
64
71
|
|
|
65
72
|
const prevBatch = showAsFolderListing
|
|
66
73
|
? content.batching?.prev
|
|
67
74
|
: showAsQueryListing
|
|
68
|
-
? querystringResults[
|
|
75
|
+
? querystringResults[id].batching?.prev
|
|
69
76
|
: null;
|
|
70
77
|
const nextBatch = showAsFolderListing
|
|
71
78
|
? content.batching?.next
|
|
72
79
|
: showAsQueryListing
|
|
73
|
-
? querystringResults[
|
|
80
|
+
? querystringResults[id].batching?.next
|
|
74
81
|
: null;
|
|
75
82
|
|
|
76
83
|
const isImageGallery =
|
|
@@ -80,7 +87,7 @@ export default function withQuerystringResults(WrappedComponent) {
|
|
|
80
87
|
useDeepCompareEffect(() => {
|
|
81
88
|
if (hasQuery) {
|
|
82
89
|
dispatch(
|
|
83
|
-
getQueryStringResults(initialPath, adaptedQuery,
|
|
90
|
+
getQueryStringResults(initialPath, adaptedQuery, id, currentPage),
|
|
84
91
|
);
|
|
85
92
|
} else if (isImageGallery && !hasQuery) {
|
|
86
93
|
// when used as image gallery, it doesn't need a query to list children
|
|
@@ -98,14 +105,16 @@ export default function withQuerystringResults(WrappedComponent) {
|
|
|
98
105
|
},
|
|
99
106
|
],
|
|
100
107
|
},
|
|
101
|
-
|
|
108
|
+
id,
|
|
102
109
|
),
|
|
103
110
|
);
|
|
104
111
|
} else {
|
|
105
112
|
dispatch(getContent(initialPath, null, null, currentPage));
|
|
106
113
|
}
|
|
114
|
+
adaptedQueryRef.current = adaptedQuery;
|
|
115
|
+
currentPageRef.current = currentPage;
|
|
107
116
|
}, [
|
|
108
|
-
|
|
117
|
+
id,
|
|
109
118
|
isImageGallery,
|
|
110
119
|
adaptedQuery,
|
|
111
120
|
hasQuery,
|
|
@@ -118,7 +127,7 @@ export default function withQuerystringResults(WrappedComponent) {
|
|
|
118
127
|
<WrappedComponent
|
|
119
128
|
{...props}
|
|
120
129
|
onPaginationChange={(e, { activePage }) => setCurrentPage(activePage)}
|
|
121
|
-
total={querystringResults?.[
|
|
130
|
+
total={querystringResults?.[id]?.total}
|
|
122
131
|
batch_size={b_size}
|
|
123
132
|
currentPage={currentPage}
|
|
124
133
|
totalPages={totalPages}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import useDeepCompareEffect from 'use-deep-compare-effect';
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
3
2
|
import { defineMessages } from 'react-intl';
|
|
4
3
|
import { compose } from 'redux';
|
|
5
4
|
|
|
@@ -60,9 +59,11 @@ const SearchBlockEdit = (props) => {
|
|
|
60
59
|
};
|
|
61
60
|
|
|
62
61
|
const { query = {} } = data || {};
|
|
63
|
-
|
|
62
|
+
// We don't need deep compare here, as this is just json serializable data.
|
|
63
|
+
const deepQuery = JSON.stringify(query);
|
|
64
|
+
useEffect(() => {
|
|
64
65
|
onTriggerSearch();
|
|
65
|
-
}, [
|
|
66
|
+
}, [deepQuery, onTriggerSearch]);
|
|
66
67
|
|
|
67
68
|
return (
|
|
68
69
|
<>
|
|
@@ -57,7 +57,7 @@ const applyDefaults = (data, root) => {
|
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
const SearchBlockView = (props) => {
|
|
60
|
-
const { data, searchData, mode = 'view', variation } = props;
|
|
60
|
+
const { id, data, searchData, mode = 'view', variation } = props;
|
|
61
61
|
|
|
62
62
|
const Layout = variation.view;
|
|
63
63
|
|
|
@@ -89,6 +89,7 @@ const SearchBlockView = (props) => {
|
|
|
89
89
|
setSelectedView={setSelectedView}
|
|
90
90
|
>
|
|
91
91
|
<ListingBody
|
|
92
|
+
id={id}
|
|
92
93
|
variation={{ ...data, ...listingBodyVariation }}
|
|
93
94
|
data={listingBodyData}
|
|
94
95
|
path={props.path}
|
|
@@ -4,6 +4,7 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|
|
4
4
|
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
5
5
|
import { compose } from 'redux';
|
|
6
6
|
import { Icon } from '@plone/volto/components';
|
|
7
|
+
import { toBackendLang } from '@plone/volto/helpers/Utils/Utils';
|
|
7
8
|
import { connect } from 'react-redux';
|
|
8
9
|
|
|
9
10
|
import leftKey from '@plone/volto/icons/left-key.svg';
|
|
@@ -82,7 +83,9 @@ const DateRangeFacet = (props) => {
|
|
|
82
83
|
noBorder
|
|
83
84
|
showClearDates
|
|
84
85
|
customCloseIcon={<CloseIcon />}
|
|
85
|
-
displayFormat={moment
|
|
86
|
+
displayFormat={moment
|
|
87
|
+
.localeData(toBackendLang(lang))
|
|
88
|
+
.longDateFormat('L')}
|
|
86
89
|
focusedInput={focused}
|
|
87
90
|
onFocusChange={(focusedInput) => setFocused(focusedInput)}
|
|
88
91
|
onDatesChange={({ startDate, endDate }) => {
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState, useMemo } from 'react';
|
|
2
|
+
import { Button, Grid } from 'semantic-ui-react';
|
|
2
3
|
import { resolveExtension } from '@plone/volto/helpers/Extensions/withBlockExtensions';
|
|
3
4
|
import config from '@plone/volto/registry';
|
|
4
5
|
import { hasNonValueOperation, hasDateOperation } from '../utils';
|
|
6
|
+
import { defineMessages, useIntl } from 'react-intl';
|
|
7
|
+
|
|
8
|
+
const messages = defineMessages({
|
|
9
|
+
moreFilters: { id: 'More filters', defaultMessage: 'More filters' },
|
|
10
|
+
lessFilters: { id: 'Less filters', defaultMessage: 'Less filters' },
|
|
11
|
+
showFilters: { id: 'Show filters', defaultMessage: 'Show filters' },
|
|
12
|
+
hideFilters: { id: 'Hide filters', defaultMessage: 'Hide filters' },
|
|
13
|
+
});
|
|
5
14
|
|
|
6
15
|
const showFacet = (index) => {
|
|
7
16
|
const { values } = index;
|
|
@@ -14,6 +23,7 @@ const showFacet = (index) => {
|
|
|
14
23
|
};
|
|
15
24
|
|
|
16
25
|
const Facets = (props) => {
|
|
26
|
+
const [hidden, setHidden] = useState(true);
|
|
17
27
|
const {
|
|
18
28
|
querystring,
|
|
19
29
|
data = {},
|
|
@@ -24,11 +34,29 @@ const Facets = (props) => {
|
|
|
24
34
|
} = props;
|
|
25
35
|
const { search } = config.blocks.blocksConfig;
|
|
26
36
|
|
|
37
|
+
const advancedFilters = useMemo(() => {
|
|
38
|
+
let count = 0;
|
|
39
|
+
for (let facetSettings of data.facets || []) {
|
|
40
|
+
if (facetSettings.advanced) {
|
|
41
|
+
count++;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (count === data.facets?.length) {
|
|
46
|
+
return 2;
|
|
47
|
+
}
|
|
48
|
+
if (count) {
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
return 0;
|
|
52
|
+
}, [data.facets]);
|
|
53
|
+
|
|
27
54
|
const FacetWrapper = facetWrapper;
|
|
28
55
|
const query_to_values = Object.assign(
|
|
29
56
|
{},
|
|
30
57
|
...(data?.query?.query?.map(({ i, v }) => ({ [i]: v })) || []),
|
|
31
58
|
);
|
|
59
|
+
const intl = useIntl();
|
|
32
60
|
|
|
33
61
|
return (
|
|
34
62
|
<>
|
|
@@ -36,6 +64,7 @@ const Facets = (props) => {
|
|
|
36
64
|
?.filter((facetSettings) => !facetSettings.hidden)
|
|
37
65
|
.map((facetSettings) => {
|
|
38
66
|
const field = facetSettings?.field?.value;
|
|
67
|
+
const isAdvanced = facetSettings?.advanced;
|
|
39
68
|
const index = querystring.indexes[field] || {};
|
|
40
69
|
const { values = {} } = index;
|
|
41
70
|
|
|
@@ -58,6 +87,7 @@ const Facets = (props) => {
|
|
|
58
87
|
|
|
59
88
|
const isMulti = facetSettings.multiple;
|
|
60
89
|
const selectedValue = facets[facetSettings?.field?.value];
|
|
90
|
+
const visible = (isAdvanced && !hidden) || !isAdvanced;
|
|
61
91
|
|
|
62
92
|
// TODO :handle changing the type of facet (multi/nonmulti)
|
|
63
93
|
|
|
@@ -74,7 +104,11 @@ const Facets = (props) => {
|
|
|
74
104
|
} = search.extensions.facetWidgets;
|
|
75
105
|
|
|
76
106
|
return FacetWrapper && (isEditMode || showFacet(index)) ? (
|
|
77
|
-
<FacetWrapper
|
|
107
|
+
<FacetWrapper
|
|
108
|
+
key={facetSettings['@id']}
|
|
109
|
+
facetSettings={facetSettings}
|
|
110
|
+
visible={visible}
|
|
111
|
+
>
|
|
78
112
|
<FacetWidget
|
|
79
113
|
facet={facetSettings}
|
|
80
114
|
choices={rewriteOptions(facetSettings?.field?.value, choices)}
|
|
@@ -90,6 +124,28 @@ const Facets = (props) => {
|
|
|
90
124
|
''
|
|
91
125
|
);
|
|
92
126
|
})}
|
|
127
|
+
{advancedFilters > 0 && (
|
|
128
|
+
<Grid.Column
|
|
129
|
+
mobile={12}
|
|
130
|
+
tablet={12}
|
|
131
|
+
computer={12}
|
|
132
|
+
className="toggle-advanced-facets"
|
|
133
|
+
>
|
|
134
|
+
<Button
|
|
135
|
+
onClick={() => {
|
|
136
|
+
setHidden((prevHidden) => !prevHidden);
|
|
137
|
+
}}
|
|
138
|
+
>
|
|
139
|
+
{hidden
|
|
140
|
+
? advancedFilters === 2
|
|
141
|
+
? intl.formatMessage(messages.showFilters)
|
|
142
|
+
: intl.formatMessage(messages.moreFilters)
|
|
143
|
+
: advancedFilters === 2
|
|
144
|
+
? intl.formatMessage(messages.hideFilters)
|
|
145
|
+
: intl.formatMessage(messages.lessFilters)}
|
|
146
|
+
</Button>
|
|
147
|
+
</Grid.Column>
|
|
148
|
+
)}
|
|
93
149
|
</>
|
|
94
150
|
);
|
|
95
151
|
};
|
|
@@ -114,15 +114,19 @@ function normalizeState({
|
|
|
114
114
|
block: id,
|
|
115
115
|
};
|
|
116
116
|
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
);
|
|
117
|
+
// Note Ideally the searchtext functionality should be restructured as being just
|
|
118
|
+
// another facet. But right now it's the same. This means that if a searchText
|
|
119
|
+
// is provided, it will override the SearchableText facet.
|
|
120
|
+
// If there is no searchText, the SearchableText in the query remains in effect.
|
|
121
|
+
// TODO eventually the searchText should be a distinct facet from SearchableText, and
|
|
122
|
+
// the two conditions could be combined, in comparison to the current state, when
|
|
123
|
+
// one overrides the other.
|
|
125
124
|
if (searchText) {
|
|
125
|
+
params.query = params.query.reduce(
|
|
126
|
+
// Remove SearchableText from query
|
|
127
|
+
(acc, kvp) => (kvp.i === 'SearchableText' ? acc : [...acc, kvp]),
|
|
128
|
+
[],
|
|
129
|
+
);
|
|
126
130
|
params.query.push({
|
|
127
131
|
i: 'SearchableText',
|
|
128
132
|
o: 'plone.app.querystring.operation.string.contains',
|
|
@@ -144,12 +148,16 @@ const getSearchFields = (searchData) => {
|
|
|
144
148
|
};
|
|
145
149
|
|
|
146
150
|
/**
|
|
147
|
-
* A
|
|
151
|
+
* A hook that will mirror the search block state to a hash location
|
|
148
152
|
*/
|
|
149
153
|
const useHashState = () => {
|
|
150
154
|
const location = useLocation();
|
|
151
155
|
const history = useHistory();
|
|
152
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Required to maintain parameter compatibility.
|
|
159
|
+
With this we will maintain support for receiving hash (#) and search (?) type parameters.
|
|
160
|
+
*/
|
|
153
161
|
const oldState = React.useMemo(() => {
|
|
154
162
|
return {
|
|
155
163
|
...qs.parse(location.search),
|
|
@@ -165,7 +173,7 @@ const useHashState = () => {
|
|
|
165
173
|
|
|
166
174
|
const setSearchData = React.useCallback(
|
|
167
175
|
(searchData) => {
|
|
168
|
-
const newParams = qs.parse(location.
|
|
176
|
+
const newParams = qs.parse(location.search);
|
|
169
177
|
|
|
170
178
|
let changed = false;
|
|
171
179
|
|
|
@@ -182,11 +190,11 @@ const useHashState = () => {
|
|
|
182
190
|
|
|
183
191
|
if (changed) {
|
|
184
192
|
history.push({
|
|
185
|
-
|
|
193
|
+
search: qs.stringify(newParams),
|
|
186
194
|
});
|
|
187
195
|
}
|
|
188
196
|
},
|
|
189
|
-
[history, oldState, location.
|
|
197
|
+
[history, oldState, location.search],
|
|
190
198
|
);
|
|
191
199
|
|
|
192
200
|
return [current, setSearchData];
|
|
@@ -278,8 +286,14 @@ const withSearch = (options) => (WrappedComponent) => {
|
|
|
278
286
|
const timeoutRef = React.useRef();
|
|
279
287
|
const facetSettings = data?.facets;
|
|
280
288
|
|
|
289
|
+
const deepQuery = JSON.stringify(data.query);
|
|
281
290
|
const onTriggerSearch = React.useCallback(
|
|
282
|
-
(
|
|
291
|
+
(
|
|
292
|
+
toSearchText = undefined,
|
|
293
|
+
toSearchFacets = undefined,
|
|
294
|
+
toSortOn = undefined,
|
|
295
|
+
toSortOrder = undefined,
|
|
296
|
+
) => {
|
|
283
297
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
284
298
|
timeoutRef.current = setTimeout(
|
|
285
299
|
() => {
|
|
@@ -287,7 +301,7 @@ const withSearch = (options) => (WrappedComponent) => {
|
|
|
287
301
|
id,
|
|
288
302
|
query: data.query || {},
|
|
289
303
|
facets: toSearchFacets || facets,
|
|
290
|
-
searchText: toSearchText,
|
|
304
|
+
searchText: toSearchText || searchText,
|
|
291
305
|
sortOn: toSortOn || sortOn,
|
|
292
306
|
sortOrder: toSortOrder || sortOrder,
|
|
293
307
|
facetSettings,
|
|
@@ -301,11 +315,14 @@ const withSearch = (options) => (WrappedComponent) => {
|
|
|
301
315
|
toSearchFacets ? inputDelay / 3 : inputDelay,
|
|
302
316
|
);
|
|
303
317
|
},
|
|
318
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
304
319
|
[
|
|
305
|
-
data.query
|
|
320
|
+
// Use deep comparison of data.query
|
|
321
|
+
deepQuery,
|
|
306
322
|
facets,
|
|
307
323
|
id,
|
|
308
324
|
setLocationSearchData,
|
|
325
|
+
searchText,
|
|
309
326
|
sortOn,
|
|
310
327
|
sortOrder,
|
|
311
328
|
facetSettings,
|
|
@@ -11,6 +11,7 @@ import { Grid, Segment } from 'semantic-ui-react';
|
|
|
11
11
|
import { Button } from 'semantic-ui-react';
|
|
12
12
|
import { flushSync } from 'react-dom';
|
|
13
13
|
import { defineMessages, useIntl } from 'react-intl';
|
|
14
|
+
import cx from 'classnames';
|
|
14
15
|
|
|
15
16
|
const messages = defineMessages({
|
|
16
17
|
searchButtonText: {
|
|
@@ -19,11 +20,22 @@ const messages = defineMessages({
|
|
|
19
20
|
},
|
|
20
21
|
});
|
|
21
22
|
|
|
22
|
-
const FacetWrapper = ({ children }) =>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const FacetWrapper = ({ children, facetSettings = {}, visible }) => {
|
|
24
|
+
const { advanced, field = {} } = facetSettings;
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Segment
|
|
28
|
+
basic
|
|
29
|
+
className={cx('facet', {
|
|
30
|
+
[`facet-index-${field.value}`]: !!field.value,
|
|
31
|
+
'advanced-facet': advanced,
|
|
32
|
+
'advanced-facet-hidden': !visible,
|
|
33
|
+
})}
|
|
34
|
+
>
|
|
35
|
+
{children}
|
|
36
|
+
</Segment>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
27
39
|
|
|
28
40
|
const LeftColumnFacets = (props) => {
|
|
29
41
|
const {
|
|
@@ -11,6 +11,7 @@ import { Grid, Segment } from 'semantic-ui-react';
|
|
|
11
11
|
import { Button } from 'semantic-ui-react';
|
|
12
12
|
import { flushSync } from 'react-dom';
|
|
13
13
|
import { defineMessages, useIntl } from 'react-intl';
|
|
14
|
+
import cx from 'classnames';
|
|
14
15
|
|
|
15
16
|
const messages = defineMessages({
|
|
16
17
|
searchButtonText: {
|
|
@@ -19,11 +20,22 @@ const messages = defineMessages({
|
|
|
19
20
|
},
|
|
20
21
|
});
|
|
21
22
|
|
|
22
|
-
const FacetWrapper = ({ children }) =>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const FacetWrapper = ({ children, facetSettings = {}, visible }) => {
|
|
24
|
+
const { advanced, field = {} } = facetSettings;
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Segment
|
|
28
|
+
basic
|
|
29
|
+
className={cx('facet', {
|
|
30
|
+
[`facet-index-${field.value}`]: !!field.value,
|
|
31
|
+
'advanced-facet': advanced,
|
|
32
|
+
'advanced-facet-hidden': !visible,
|
|
33
|
+
})}
|
|
34
|
+
>
|
|
35
|
+
{children}
|
|
36
|
+
</Segment>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
27
39
|
|
|
28
40
|
const RightColumnFacets = (props) => {
|
|
29
41
|
const {
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
SortOn,
|
|
12
12
|
ViewSwitcher,
|
|
13
13
|
} from '../components';
|
|
14
|
+
import cx from 'classnames';
|
|
14
15
|
|
|
15
16
|
const messages = defineMessages({
|
|
16
17
|
searchButtonText: {
|
|
@@ -19,11 +20,24 @@ const messages = defineMessages({
|
|
|
19
20
|
},
|
|
20
21
|
});
|
|
21
22
|
|
|
22
|
-
const FacetWrapper = ({ children }) =>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const FacetWrapper = ({ children, facetSettings = {}, visible }) => {
|
|
24
|
+
const { advanced, field = {} } = facetSettings;
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Grid.Column
|
|
28
|
+
mobile={12}
|
|
29
|
+
tablet={4}
|
|
30
|
+
computer={3}
|
|
31
|
+
className={cx('facet', {
|
|
32
|
+
[`facet-index-${field.value}`]: !!field.value,
|
|
33
|
+
'advanced-facet': advanced,
|
|
34
|
+
'advanced-facet-hidden': !visible,
|
|
35
|
+
})}
|
|
36
|
+
>
|
|
37
|
+
{children}
|
|
38
|
+
</Grid.Column>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
27
41
|
|
|
28
42
|
const TopSideFacets = (props) => {
|
|
29
43
|
const {
|
|
@@ -117,6 +131,7 @@ const TopSideFacets = (props) => {
|
|
|
117
131
|
<ViewSwitcher {...props} />
|
|
118
132
|
)}
|
|
119
133
|
</div>
|
|
134
|
+
|
|
120
135
|
{data.facets?.length > 0 && (
|
|
121
136
|
<div className="facets">
|
|
122
137
|
{data.facetsTitle && <h3>{data.facetsTitle}</h3>}
|
|
@@ -136,6 +151,7 @@ const TopSideFacets = (props) => {
|
|
|
136
151
|
</Grid>
|
|
137
152
|
</div>
|
|
138
153
|
)}
|
|
154
|
+
|
|
139
155
|
<SearchDetails
|
|
140
156
|
text={searchedText}
|
|
141
157
|
total={totalItems}
|
|
@@ -88,6 +88,15 @@ const messages = defineMessages({
|
|
|
88
88
|
defaultMessage:
|
|
89
89
|
'Hidden facets will still filter the results if proper parameters are passed in URLs',
|
|
90
90
|
},
|
|
91
|
+
advancedFacetTitle: {
|
|
92
|
+
id: 'Advanced facet?',
|
|
93
|
+
defaultMessage: 'Advanced facet?',
|
|
94
|
+
},
|
|
95
|
+
advancedFacetDescription: {
|
|
96
|
+
id: 'Advanced facets are initially hidden and displayed on demand',
|
|
97
|
+
defaultMessage:
|
|
98
|
+
'Advanced facets are initially hidden and displayed on demand',
|
|
99
|
+
},
|
|
91
100
|
facetWidget: {
|
|
92
101
|
id: 'Facet widget',
|
|
93
102
|
defaultMessage: 'Facet widget',
|
|
@@ -131,7 +140,7 @@ const FacetSchema = ({ intl }) => ({
|
|
|
131
140
|
{
|
|
132
141
|
id: 'default',
|
|
133
142
|
title: 'Default',
|
|
134
|
-
fields: ['title', 'field', 'type', 'hidden'],
|
|
143
|
+
fields: ['title', 'field', 'type', 'hidden', 'advanced'],
|
|
135
144
|
},
|
|
136
145
|
],
|
|
137
146
|
properties: {
|
|
@@ -170,6 +179,12 @@ const FacetSchema = ({ intl }) => ({
|
|
|
170
179
|
default: false,
|
|
171
180
|
description: intl.formatMessage(messages.hideFacetDescription),
|
|
172
181
|
},
|
|
182
|
+
advanced: {
|
|
183
|
+
type: 'boolean',
|
|
184
|
+
title: intl.formatMessage(messages.advancedFacetTitle),
|
|
185
|
+
default: false,
|
|
186
|
+
description: intl.formatMessage(messages.advancedFacetDescription),
|
|
187
|
+
},
|
|
173
188
|
type: {
|
|
174
189
|
title: intl.formatMessage(messages.facetWidget),
|
|
175
190
|
choices: config.blocks.blocksConfig.search.extensions.facetWidgets.types.map(
|