@eeacms/volto-eea-website-policy 2.1.0 → 2.2.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 +16 -0
- package/package.json +4 -5
- package/src/components/Blocks/ContextNavigation/ContextNavigationEdit.jsx +31 -17
- package/src/components/Blocks/ContextNavigation/ContextNavigationEdit.test.jsx +37 -13
- package/src/components/Blocks/ContextNavigation/ContextNavigationView.jsx +7 -10
- package/src/components/Blocks/ContextNavigation/ContextNavigationView.test.jsx +33 -12
- package/src/components/Blocks/ContextNavigation/index.js +2 -0
- package/src/components/Blocks/ContextNavigation/schema.js +10 -3
- package/src/components/Blocks/ContextNavigation/variations/Accordion.jsx +175 -0
- package/src/components/Blocks/ContextNavigation/variations/Default.jsx +9 -0
- package/src/components/Blocks/ContextNavigation/variations/index.js +18 -0
- package/src/index.js +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
### [2.2.0](https://github.com/eea/volto-eea-website-policy/compare/2.1.1...2.2.0) - 26 August 2024
|
|
8
|
+
|
|
9
|
+
#### :rocket: New Features
|
|
10
|
+
|
|
11
|
+
- feat: context navigation portlet - refs #270999 [ichim-david - [`ca045b4`](https://github.com/eea/volto-eea-website-policy/commit/ca045b464c16aa5f1107204138e2ebfadf8669e3)]
|
|
12
|
+
|
|
13
|
+
#### :hammer_and_wrench: Others
|
|
14
|
+
|
|
15
|
+
### [2.1.1](https://github.com/eea/volto-eea-website-policy/compare/2.1.0...2.1.1) - 30 May 2024
|
|
16
|
+
|
|
17
|
+
#### :house: Internal changes
|
|
18
|
+
|
|
19
|
+
- chore: Fix @plone-collective/volto-authomatic dependencies [alin - [`3c11765`](https://github.com/eea/volto-eea-website-policy/commit/3c117655f254d68b89a9748216b405c93ec18a0e)]
|
|
20
|
+
|
|
21
|
+
#### :hammer_and_wrench: Others
|
|
22
|
+
|
|
7
23
|
### [2.1.0](https://github.com/eea/volto-eea-website-policy/compare/2.0.0...2.1.0) - 9 May 2024
|
|
8
24
|
|
|
9
25
|
#### :rocket: New Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eeacms/volto-eea-website-policy",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "@eeacms/volto-eea-website-policy: Volto add-on",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"author": "European Environment Agency: IDM2 A-Team",
|
|
@@ -17,15 +17,14 @@
|
|
|
17
17
|
"url": "git@github.com:eea/volto-eea-website-policy.git"
|
|
18
18
|
},
|
|
19
19
|
"addons": [
|
|
20
|
+
"@eeacms/volto-block-toc",
|
|
20
21
|
"@eeacms/volto-widget-dataprovenance",
|
|
21
22
|
"@plone-collective/volto-authomatic"
|
|
22
23
|
],
|
|
23
24
|
"dependencies": {
|
|
25
|
+
"@eeacms/volto-block-toc": "*",
|
|
24
26
|
"@eeacms/volto-widget-dataprovenance": "*",
|
|
25
|
-
"@plone-collective/volto-authomatic": "
|
|
26
|
-
},
|
|
27
|
-
"resolutions": {
|
|
28
|
-
"@plone-collective/volto-authomatic": "2.0.1"
|
|
27
|
+
"@plone-collective/volto-authomatic": "*"
|
|
29
28
|
},
|
|
30
29
|
"devDependencies": {
|
|
31
30
|
"@cypress/code-coverage": "^3.10.0",
|
|
@@ -1,31 +1,45 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { EditSchema } from './schema';
|
|
3
|
-
import {
|
|
3
|
+
import { SidebarPortal } from '@plone/volto/components';
|
|
4
|
+
import BlockDataForm from '@plone/volto/components/manage/Form/BlockDataForm';
|
|
5
|
+
|
|
4
6
|
import ContextNavigationView from './ContextNavigationView';
|
|
5
7
|
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
+
const ContextNavigationFillEdit = (props) => {
|
|
9
|
+
const contentTypes = props.properties?.['@components']?.types;
|
|
10
|
+
const availableTypes = React.useMemo(
|
|
11
|
+
() => contentTypes?.map((type) => [type.id, type.title || type.name]),
|
|
12
|
+
[contentTypes],
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const schema = React.useMemo(
|
|
16
|
+
() => EditSchema({ availableTypes }),
|
|
17
|
+
[availableTypes],
|
|
18
|
+
);
|
|
19
|
+
|
|
8
20
|
return (
|
|
9
21
|
<>
|
|
10
22
|
<h3>Context navigation</h3>
|
|
11
23
|
<ContextNavigationView {...props} />{' '}
|
|
12
24
|
<SidebarPortal selected={props.selected}>
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
<BlockDataForm
|
|
26
|
+
schema={schema}
|
|
27
|
+
title={schema.title}
|
|
28
|
+
onChangeField={(id, value) => {
|
|
29
|
+
props.onChangeBlock(props.block, {
|
|
30
|
+
...props.data,
|
|
31
|
+
[id]: value,
|
|
32
|
+
});
|
|
33
|
+
}}
|
|
34
|
+
onChangeBlock={props.onChangeBlock}
|
|
35
|
+
formData={props.data}
|
|
36
|
+
block={props.block}
|
|
37
|
+
navRoot={props.navRoot}
|
|
38
|
+
contentType={props.contentType}
|
|
39
|
+
/>
|
|
26
40
|
</SidebarPortal>
|
|
27
41
|
</>
|
|
28
42
|
);
|
|
29
43
|
};
|
|
30
44
|
|
|
31
|
-
export default
|
|
45
|
+
export default ContextNavigationFillEdit;
|
|
@@ -2,6 +2,8 @@ import React from 'react';
|
|
|
2
2
|
import { render, fireEvent } from '@testing-library/react';
|
|
3
3
|
import ContextNavigationEdit from './ContextNavigationEdit';
|
|
4
4
|
import { Router } from 'react-router-dom';
|
|
5
|
+
import { Provider } from 'react-intl-redux';
|
|
6
|
+
import configureStore from 'redux-mock-store';
|
|
5
7
|
import { createMemoryHistory } from 'history';
|
|
6
8
|
import '@testing-library/jest-dom/extend-expect';
|
|
7
9
|
|
|
@@ -12,12 +14,13 @@ jest.mock('@plone/volto/components', () => ({
|
|
|
12
14
|
<input id="test" onChange={onChangeField} />
|
|
13
15
|
</div>
|
|
14
16
|
),
|
|
15
|
-
SidebarPortal: ({ children }) =>
|
|
16
|
-
|
|
17
|
-
<div>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
SidebarPortal: ({ children, selected }) =>
|
|
18
|
+
selected ? (
|
|
19
|
+
<div>
|
|
20
|
+
<div>SidebarPortal</div>
|
|
21
|
+
{children}
|
|
22
|
+
</div>
|
|
23
|
+
) : null,
|
|
21
24
|
}));
|
|
22
25
|
|
|
23
26
|
jest.mock('@plone/volto/components/theme/Navigation/ContextNavigation', () => {
|
|
@@ -29,27 +32,48 @@ jest.mock('@plone/volto/components/theme/Navigation/ContextNavigation', () => {
|
|
|
29
32
|
};
|
|
30
33
|
});
|
|
31
34
|
|
|
35
|
+
jest.mock('@plone/volto/helpers', () => ({
|
|
36
|
+
withBlockExtensions: jest.fn((Component) => Component),
|
|
37
|
+
emptyBlocksForm: jest.fn(),
|
|
38
|
+
getBlocksLayoutFieldname: () => 'blocks_layout',
|
|
39
|
+
withVariationSchemaEnhancer: jest.fn((Component) => Component),
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
const mockStore = configureStore();
|
|
43
|
+
const store = mockStore({
|
|
44
|
+
intl: {
|
|
45
|
+
locale: 'en',
|
|
46
|
+
messages: {},
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
32
50
|
describe('ContextNavigationEdit', () => {
|
|
33
51
|
it('renders corectly', () => {
|
|
34
52
|
const history = createMemoryHistory();
|
|
35
53
|
const { getByText, queryByText } = render(
|
|
36
|
-
<
|
|
37
|
-
<
|
|
38
|
-
|
|
54
|
+
<Provider store={store}>
|
|
55
|
+
<Router history={history}>
|
|
56
|
+
<ContextNavigationEdit selected={false} />
|
|
57
|
+
</Router>
|
|
58
|
+
,
|
|
59
|
+
</Provider>,
|
|
39
60
|
);
|
|
40
61
|
|
|
41
62
|
expect(getByText('Context navigation')).toBeInTheDocument();
|
|
42
63
|
expect(getByText('ConnectedContextNavigation')).toBeInTheDocument();
|
|
43
64
|
expect(queryByText('InlineForm')).toBeNull();
|
|
44
|
-
expect(
|
|
65
|
+
expect(queryByText('SidebarPortal')).toBeNull();
|
|
45
66
|
});
|
|
46
67
|
|
|
47
68
|
it('renders corectly', () => {
|
|
48
69
|
const history = createMemoryHistory();
|
|
49
70
|
const { container, getByText } = render(
|
|
50
|
-
<
|
|
51
|
-
<
|
|
52
|
-
|
|
71
|
+
<Provider store={store}>
|
|
72
|
+
<Router history={history}>
|
|
73
|
+
<ContextNavigationEdit selected={true} onChangeBlock={() => {}} />
|
|
74
|
+
</Router>
|
|
75
|
+
,
|
|
76
|
+
</Provider>,
|
|
53
77
|
);
|
|
54
78
|
|
|
55
79
|
expect(getByText('Context navigation')).toBeInTheDocument();
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import { flattenToAppURL, withBlockExtensions } from '@plone/volto/helpers';
|
|
3
|
+
import DefaultTemplate from './variations/Default';
|
|
4
4
|
|
|
5
|
-
const ContextNavigationView = (
|
|
5
|
+
const ContextNavigationView = (props = {}) => {
|
|
6
|
+
const { variation, data = {} } = props;
|
|
6
7
|
const navProps = { ...data };
|
|
7
8
|
const root_path = data?.root_node?.[0]?.['@id'];
|
|
8
9
|
if (root_path) navProps['root_path'] = flattenToAppURL(root_path);
|
|
9
|
-
|
|
10
|
-
return
|
|
11
|
-
<>
|
|
12
|
-
<ConnectedContextNavigation params={navProps} />
|
|
13
|
-
</>
|
|
14
|
-
);
|
|
10
|
+
const Renderer = variation?.view ?? DefaultTemplate;
|
|
11
|
+
return <Renderer params={navProps} />;
|
|
15
12
|
};
|
|
16
13
|
|
|
17
|
-
export default ContextNavigationView;
|
|
14
|
+
export default withBlockExtensions(ContextNavigationView);
|
|
@@ -2,6 +2,8 @@ import React from 'react';
|
|
|
2
2
|
import { render } from '@testing-library/react';
|
|
3
3
|
import ContextNavigationView from './ContextNavigationView';
|
|
4
4
|
import { Router } from 'react-router-dom';
|
|
5
|
+
import { Provider } from 'react-intl-redux';
|
|
6
|
+
import configureStore from 'redux-mock-store';
|
|
5
7
|
import { createMemoryHistory } from 'history';
|
|
6
8
|
import '@testing-library/jest-dom/extend-expect';
|
|
7
9
|
|
|
@@ -14,6 +16,21 @@ jest.mock('@plone/volto/components/theme/Navigation/ContextNavigation', () => {
|
|
|
14
16
|
};
|
|
15
17
|
});
|
|
16
18
|
|
|
19
|
+
jest.mock('@plone/volto/helpers', () => ({
|
|
20
|
+
withBlockExtensions: jest.fn((Component) => Component),
|
|
21
|
+
emptyBlocksForm: jest.fn(),
|
|
22
|
+
getBlocksLayoutFieldname: () => 'blocks_layout',
|
|
23
|
+
flattenToAppURL: () => '',
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
const mockStore = configureStore();
|
|
27
|
+
const store = mockStore({
|
|
28
|
+
intl: {
|
|
29
|
+
locale: 'en',
|
|
30
|
+
messages: {},
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
17
34
|
describe('ContextNavigationView', () => {
|
|
18
35
|
let history;
|
|
19
36
|
beforeEach(() => {
|
|
@@ -22,9 +39,11 @@ describe('ContextNavigationView', () => {
|
|
|
22
39
|
|
|
23
40
|
it('renders corectly', () => {
|
|
24
41
|
const { container } = render(
|
|
25
|
-
<
|
|
26
|
-
<
|
|
27
|
-
|
|
42
|
+
<Provider store={store}>
|
|
43
|
+
<Router history={history}>
|
|
44
|
+
<ContextNavigationView />
|
|
45
|
+
</Router>
|
|
46
|
+
</Provider>,
|
|
28
47
|
);
|
|
29
48
|
|
|
30
49
|
expect(container.firstChild).toHaveTextContent(
|
|
@@ -34,17 +53,19 @@ describe('ContextNavigationView', () => {
|
|
|
34
53
|
|
|
35
54
|
it('renders corectly', () => {
|
|
36
55
|
const { container } = render(
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
56
|
+
<Provider store={store}>
|
|
57
|
+
<Router history={history}>
|
|
58
|
+
<ContextNavigationView
|
|
59
|
+
data={{
|
|
60
|
+
navProps: { root_path: 'https://localhost:3000/test' },
|
|
61
|
+
root_node: [{ '@id': 'root_node' }],
|
|
62
|
+
}}
|
|
63
|
+
/>
|
|
64
|
+
</Router>
|
|
65
|
+
</Provider>,
|
|
45
66
|
);
|
|
46
67
|
expect(container.firstChild).toHaveTextContent(
|
|
47
|
-
'ConnectedContextNavigation
|
|
68
|
+
'ConnectedContextNavigation',
|
|
48
69
|
);
|
|
49
70
|
});
|
|
50
71
|
});
|
|
@@ -2,6 +2,7 @@ import codeSVG from '@plone/volto/icons/code.svg';
|
|
|
2
2
|
import ContextNavigationEdit from './ContextNavigationEdit';
|
|
3
3
|
import ContextNavigationView from './ContextNavigationView';
|
|
4
4
|
import BlockSettingsSchema from '@plone/volto/components/manage/Blocks/Block/Schema';
|
|
5
|
+
import variations from './variations';
|
|
5
6
|
|
|
6
7
|
const applyConfig = (config) => {
|
|
7
8
|
config.blocks.blocksConfig.contextNavigation = {
|
|
@@ -13,6 +14,7 @@ const applyConfig = (config) => {
|
|
|
13
14
|
edit: ContextNavigationEdit,
|
|
14
15
|
schema: BlockSettingsSchema,
|
|
15
16
|
restricted: true,
|
|
17
|
+
variations,
|
|
16
18
|
mostUsed: true,
|
|
17
19
|
blockHasOwnFocusManagement: true,
|
|
18
20
|
sidebarTab: 1,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const EditSchema = () => {
|
|
1
|
+
export const EditSchema = ({ availableTypes }) => {
|
|
2
2
|
return {
|
|
3
3
|
title: 'Navigation',
|
|
4
4
|
fieldsets: [
|
|
@@ -8,6 +8,7 @@ export const EditSchema = () => {
|
|
|
8
8
|
fields: [
|
|
9
9
|
'name',
|
|
10
10
|
'root_node',
|
|
11
|
+
'portal_type',
|
|
11
12
|
'includeTop',
|
|
12
13
|
'currentFolderOnly',
|
|
13
14
|
'topLevel',
|
|
@@ -33,6 +34,12 @@ export const EditSchema = () => {
|
|
|
33
34
|
mode: 'link',
|
|
34
35
|
selectedItemAttrs: ['Title', 'Description'],
|
|
35
36
|
},
|
|
37
|
+
portal_type: {
|
|
38
|
+
title: 'Filter children',
|
|
39
|
+
description: 'Only show child items of this content type',
|
|
40
|
+
choices: availableTypes,
|
|
41
|
+
isMulti: true,
|
|
42
|
+
},
|
|
36
43
|
includeTop: {
|
|
37
44
|
title: 'Include top node',
|
|
38
45
|
description:
|
|
@@ -61,7 +68,7 @@ export const EditSchema = () => {
|
|
|
61
68
|
default: 0,
|
|
62
69
|
},
|
|
63
70
|
no_icons: {
|
|
64
|
-
title: '
|
|
71
|
+
title: 'Suppress icons',
|
|
65
72
|
description:
|
|
66
73
|
'If enabled, the portlet will not show document type icons.',
|
|
67
74
|
type: 'boolean',
|
|
@@ -72,7 +79,7 @@ export const EditSchema = () => {
|
|
|
72
79
|
"Enter a valid scale name (see 'Image Handling' control panel) to override (e.g. icon, tile, thumb, mini, preview, ... ). Leave empty to use default (see 'Site' control panel).",
|
|
73
80
|
},
|
|
74
81
|
no_thumbs: {
|
|
75
|
-
title: '
|
|
82
|
+
title: 'Suppress thumbs',
|
|
76
83
|
type: 'boolean',
|
|
77
84
|
description: 'If enabled, the portlet will not show thumbs.',
|
|
78
85
|
},
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import cx from 'classnames';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { defineMessages, useIntl } from 'react-intl';
|
|
5
|
+
import { withRouter } from 'react-router';
|
|
6
|
+
import { compose } from 'redux';
|
|
7
|
+
import { Accordion } from 'semantic-ui-react';
|
|
8
|
+
|
|
9
|
+
import Slugger from 'github-slugger';
|
|
10
|
+
|
|
11
|
+
import { Icon, UniversalLink } from '@plone/volto/components';
|
|
12
|
+
import { withContentNavigation } from '@plone/volto/components/theme/Navigation/withContentNavigation';
|
|
13
|
+
import withEEASideMenu from '@eeacms/volto-block-toc/hocs/withEEASideMenu';
|
|
14
|
+
import { flattenToAppURL } from '@plone/volto/helpers';
|
|
15
|
+
|
|
16
|
+
import downIcon from '@plone/volto/icons/down-key.svg';
|
|
17
|
+
import upIcon from '@plone/volto/icons/up-key.svg';
|
|
18
|
+
|
|
19
|
+
const messages = defineMessages({
|
|
20
|
+
navigation: {
|
|
21
|
+
id: 'Navigation',
|
|
22
|
+
defaultMessage: 'Navigation',
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const AccordionNavigation = ({
|
|
27
|
+
navigation = {},
|
|
28
|
+
device,
|
|
29
|
+
isMenuOpenOnOutsideClick,
|
|
30
|
+
}) => {
|
|
31
|
+
const { items = [], title, has_custom_name } = navigation;
|
|
32
|
+
const intl = useIntl();
|
|
33
|
+
const navOpen = ['mobile', 'tablet'].includes(device) ? false : true;
|
|
34
|
+
const [isNavOpen, setIsNavOpen] = React.useState(navOpen);
|
|
35
|
+
const [activeItems, setActiveItems] = React.useState({});
|
|
36
|
+
|
|
37
|
+
const onClickSummary = React.useCallback((e) => {
|
|
38
|
+
e.preventDefault();
|
|
39
|
+
setIsNavOpen((prev) => !prev);
|
|
40
|
+
}, []);
|
|
41
|
+
|
|
42
|
+
React.useEffect(() => {
|
|
43
|
+
if (isMenuOpenOnOutsideClick === false) setIsNavOpen(false);
|
|
44
|
+
}, [isMenuOpenOnOutsideClick]);
|
|
45
|
+
|
|
46
|
+
const onKeyDownSummary = React.useCallback(
|
|
47
|
+
(e) => {
|
|
48
|
+
if (e.keyCode === 13 || e.keyCode === 32) {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
onClickSummary(e);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
[onClickSummary],
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const renderItems = ({ item, level = 0 }) => {
|
|
57
|
+
const {
|
|
58
|
+
title,
|
|
59
|
+
href,
|
|
60
|
+
is_current,
|
|
61
|
+
is_in_path,
|
|
62
|
+
items: childItems,
|
|
63
|
+
type,
|
|
64
|
+
} = item;
|
|
65
|
+
const hasChildItems = childItems && childItems.length > 0;
|
|
66
|
+
const normalizedTitle = Slugger.slug(title);
|
|
67
|
+
|
|
68
|
+
const checkIfActive = () => {
|
|
69
|
+
return activeItems[href] !== undefined ? activeItems[href] : is_in_path;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const isActive = checkIfActive();
|
|
73
|
+
|
|
74
|
+
const handleTitleClick = () => {
|
|
75
|
+
setActiveItems((prev) => ({ ...prev, [href]: !isActive }));
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<li
|
|
80
|
+
className={cx({
|
|
81
|
+
is_in_path,
|
|
82
|
+
'accordion-list-title': !hasChildItems,
|
|
83
|
+
'accordion-list-item': hasChildItems,
|
|
84
|
+
})}
|
|
85
|
+
key={href}
|
|
86
|
+
>
|
|
87
|
+
{hasChildItems ? (
|
|
88
|
+
<Accordion className="default">
|
|
89
|
+
<Accordion.Title
|
|
90
|
+
active={isActive}
|
|
91
|
+
as={'button'}
|
|
92
|
+
aria-expanded={isActive}
|
|
93
|
+
onClick={handleTitleClick}
|
|
94
|
+
aria-controls={`accordion-content-${normalizedTitle}`}
|
|
95
|
+
id={`accordion-title-${normalizedTitle}`}
|
|
96
|
+
>
|
|
97
|
+
<span className="title-text">{title}</span>
|
|
98
|
+
<Icon name={isActive ? upIcon : downIcon} size="32px" />
|
|
99
|
+
</Accordion.Title>
|
|
100
|
+
<Accordion.Content
|
|
101
|
+
active={isActive}
|
|
102
|
+
id={`accordion-content-${normalizedTitle}`}
|
|
103
|
+
aria-labelledby={`accordion-title-${normalizedTitle}`}
|
|
104
|
+
role="region"
|
|
105
|
+
>
|
|
106
|
+
<ul className="accordion-list">
|
|
107
|
+
{childItems.map((child) =>
|
|
108
|
+
renderItems({ item: child, level: level + 1 }),
|
|
109
|
+
)}
|
|
110
|
+
</ul>
|
|
111
|
+
</Accordion.Content>
|
|
112
|
+
</Accordion>
|
|
113
|
+
) : (
|
|
114
|
+
<UniversalLink
|
|
115
|
+
href={flattenToAppURL(href)}
|
|
116
|
+
className={cx(`title-link contenttype-${type}`, {
|
|
117
|
+
current: is_current,
|
|
118
|
+
in_path: is_in_path,
|
|
119
|
+
})}
|
|
120
|
+
>
|
|
121
|
+
{title}
|
|
122
|
+
</UniversalLink>
|
|
123
|
+
)}
|
|
124
|
+
</li>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
return items.length ? (
|
|
129
|
+
<>
|
|
130
|
+
<nav className="context-navigation">
|
|
131
|
+
<details open={isNavOpen}>
|
|
132
|
+
{/* eslint-disable-next-line */}
|
|
133
|
+
<summary
|
|
134
|
+
className="context-navigation-header accordion-header"
|
|
135
|
+
onClick={onClickSummary}
|
|
136
|
+
onKeyDown={onKeyDownSummary}
|
|
137
|
+
>
|
|
138
|
+
{has_custom_name ? title : intl.formatMessage(messages.navigation)}
|
|
139
|
+
<Icon name={isNavOpen ? upIcon : downIcon} size="40px" />
|
|
140
|
+
</summary>
|
|
141
|
+
<ul className="context-navigation-list accordion-list">
|
|
142
|
+
{items.map((item) => renderItems({ item }))}
|
|
143
|
+
</ul>
|
|
144
|
+
</details>
|
|
145
|
+
</nav>
|
|
146
|
+
</>
|
|
147
|
+
) : null;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
AccordionNavigation.propTypes = {
|
|
151
|
+
/**
|
|
152
|
+
* Navigation tree returned from @contextnavigation restapi endpoint
|
|
153
|
+
*/
|
|
154
|
+
navigation: PropTypes.shape({
|
|
155
|
+
items: PropTypes.arrayOf(
|
|
156
|
+
PropTypes.shape({
|
|
157
|
+
title: PropTypes.string,
|
|
158
|
+
url: PropTypes.string,
|
|
159
|
+
href: PropTypes.string,
|
|
160
|
+
is_current: PropTypes.bool,
|
|
161
|
+
is_in_path: PropTypes.bool,
|
|
162
|
+
items: PropTypes.array,
|
|
163
|
+
type: PropTypes.string,
|
|
164
|
+
}),
|
|
165
|
+
),
|
|
166
|
+
has_custom_name: PropTypes.bool,
|
|
167
|
+
title: PropTypes.string,
|
|
168
|
+
}),
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export default compose(
|
|
172
|
+
withRouter,
|
|
173
|
+
withContentNavigation,
|
|
174
|
+
withEEASideMenu,
|
|
175
|
+
)(AccordionNavigation);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ConnectedContextNavigation from '@plone/volto/components/theme/Navigation/ContextNavigation';
|
|
3
|
+
|
|
4
|
+
const Default = (props) => {
|
|
5
|
+
const { params } = props;
|
|
6
|
+
return <ConnectedContextNavigation params={params} />;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default Default;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import Accordion from './Accordion';
|
|
2
|
+
import Default from './Default';
|
|
3
|
+
|
|
4
|
+
const contextBlockVariations = [
|
|
5
|
+
{
|
|
6
|
+
id: 'default',
|
|
7
|
+
title: 'Listing (default)',
|
|
8
|
+
view: Default,
|
|
9
|
+
isDefault: true,
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
id: 'accordion',
|
|
13
|
+
title: 'Accordion',
|
|
14
|
+
view: Accordion,
|
|
15
|
+
},
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export default contextBlockVariations;
|
package/src/index.js
CHANGED
|
@@ -112,7 +112,7 @@ const applyConfig = (config) => {
|
|
|
112
112
|
...config.settings.eea,
|
|
113
113
|
rolesWhoCanChangeLayout: ['Manager'],
|
|
114
114
|
};
|
|
115
|
-
|
|
115
|
+
config.settings.eea.rolesWhoCanChangeLayout = ['Manager'];
|
|
116
116
|
|
|
117
117
|
// Custom blocks
|
|
118
118
|
// context navigation
|
|
@@ -135,6 +135,10 @@ const applyConfig = (config) => {
|
|
|
135
135
|
}
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
+
if (config.blocks.blocksConfig.contextNavigation) {
|
|
139
|
+
config.blocks.blocksConfig.contextNavigation.restricted = false;
|
|
140
|
+
}
|
|
141
|
+
|
|
138
142
|
// Override blocks config
|
|
139
143
|
Object.keys(overrideBlocks).forEach((block) => {
|
|
140
144
|
if (config.blocks.blocksConfig[block]) {
|