@plone/volto 17.0.0-alpha.17 → 17.0.0-alpha.19
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 +57 -0
- package/cypress/support/commands.js +17 -0
- package/locales/ca/LC_MESSAGES/volto.po +39 -0
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +39 -0
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +39 -0
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +39 -0
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +39 -0
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +39 -0
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +39 -0
- package/locales/fr.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +40 -1
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +39 -0
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +39 -0
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +39 -0
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +39 -0
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +39 -0
- package/locales/ro.json +1 -1
- package/locales/volto.pot +39 -0
- package/locales/zh_CN/LC_MESSAGES/volto.po +39 -0
- package/locales/zh_CN.json +1 -1
- package/package.json +2 -2
- package/packages/volto-slate/package.json +1 -1
- package/packages/volto-slate/src/blocks/Table/TableBlockEdit.jsx +21 -212
- package/packages/volto-slate/src/blocks/Table/schema.js +122 -0
- package/packages/volto-slate/src/editor/plugins/StyleMenu/utils.js +14 -5
- package/packages/volto-slate/src/utils/blocks.js +7 -0
- package/packages/volto-slate/src/widgets/RichTextWidget.jsx +15 -8
- package/src/components/index.js +1 -0
- package/src/components/manage/Blocks/Search/components/Facets.jsx +6 -2
- package/src/components/manage/Blocks/Search/components/SearchInput.jsx +9 -2
- package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +12 -1
- package/src/components/manage/Blocks/ToC/Schema.jsx +5 -1
- package/src/components/manage/Blocks/ToC/variations/HorizontalMenu.jsx +142 -8
- package/src/components/manage/LinksToItem/LinksToItem.jsx +209 -0
- package/src/components/manage/LinksToItem/LinksToItem.test.jsx +97 -0
- package/src/components/manage/Toolbar/More.jsx +15 -0
- package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +1 -1
- package/src/components/theme/Breadcrumbs/Breadcrumbs.jsx +52 -99
- package/src/components/theme/Breadcrumbs/Breadcrumbs.stories.jsx +14 -13
- package/src/components/theme/Comments/CommentEditModal.jsx +63 -115
- package/src/components/theme/ContactForm/ContactForm.jsx +108 -192
- package/src/components/theme/ContactForm/ContactForm.stories.jsx +1 -1
- package/src/components/theme/ContactForm/ContactForm.test.jsx +2 -3
- package/src/components/theme/Login/Login.jsx +1 -1
- package/src/components/theme/SearchWidget/SearchWidget.jsx +38 -98
- package/src/components/theme/View/LinkView.jsx +51 -79
- package/src/config/NonContentRoutes.jsx +1 -0
- package/src/config/index.js +2 -0
- package/src/config/server.js +2 -0
- package/src/express-middleware/ok.js +16 -0
- package/src/hooks/client/useClient.js +11 -0
- package/src/hooks/index.js +1 -0
- package/src/routes.js +9 -0
- package/theme/themes/pastanaga/extras/main.less +2 -1
- package/theme/themes/pastanaga/extras/toc.less +29 -0
- package/news/4351.bugfix +0 -1
- package/news/4725.bugfix +0 -1
- package/news/4932.bugfix +0 -1
- package/news/4941.documentation +0 -1
- package/news/4951.breaking +0 -1
- package/news/4962.feature +0 -1
- package/news/4964.bugfix +0 -1
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LinksToItem component
|
|
3
|
+
* @module components/manage/LinksToItem/LinksToItem
|
|
4
|
+
*/
|
|
5
|
+
import { useEffect } from 'react';
|
|
6
|
+
import { find } from 'lodash';
|
|
7
|
+
import { Helmet } from '@plone/volto/helpers';
|
|
8
|
+
import { Link } from 'react-router-dom';
|
|
9
|
+
import { Portal } from 'react-portal';
|
|
10
|
+
import { Container, Segment, Table } from 'semantic-ui-react';
|
|
11
|
+
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
|
12
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
13
|
+
import { getContent, queryRelations } from '@plone/volto/actions';
|
|
14
|
+
import {
|
|
15
|
+
Icon as IconNext,
|
|
16
|
+
Toolbar,
|
|
17
|
+
UniversalLink,
|
|
18
|
+
} from '@plone/volto/components';
|
|
19
|
+
|
|
20
|
+
import { getBaseUrl } from '@plone/volto/helpers';
|
|
21
|
+
import backSVG from '@plone/volto/icons/back.svg';
|
|
22
|
+
import settingsSVG from '@plone/volto/icons/settings.svg';
|
|
23
|
+
|
|
24
|
+
const messages = defineMessages({
|
|
25
|
+
back: {
|
|
26
|
+
id: 'Back',
|
|
27
|
+
defaultMessage: 'Back',
|
|
28
|
+
},
|
|
29
|
+
linkstoitem: {
|
|
30
|
+
id: 'Links and references',
|
|
31
|
+
defaultMessage: 'Links and references',
|
|
32
|
+
},
|
|
33
|
+
helpLinkRelationsControlPanel: {
|
|
34
|
+
id: 'Overview of relations of all content items',
|
|
35
|
+
defaultMessage: 'Overview of relations of all content items',
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const LinksToItem = (props) => {
|
|
40
|
+
const intl = useIntl();
|
|
41
|
+
const dispatch = useDispatch();
|
|
42
|
+
const pathname = props.location.pathname;
|
|
43
|
+
const itempath = getBaseUrl(pathname);
|
|
44
|
+
|
|
45
|
+
const title = useSelector((state) => state.content.data?.title || '');
|
|
46
|
+
const myrelations = useSelector(
|
|
47
|
+
(state) => state.relations.subrequests[itempath]?.relations,
|
|
48
|
+
);
|
|
49
|
+
const actions = useSelector((state) => state.actions?.actions ?? {});
|
|
50
|
+
const ploneSetupAction = find(actions.user, {
|
|
51
|
+
id: 'plone_setup',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
dispatch(queryRelations(null, false, itempath, null, [itempath]));
|
|
56
|
+
}, [dispatch, itempath]);
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (!title) dispatch(getContent(itempath));
|
|
60
|
+
}, [dispatch, itempath, title]);
|
|
61
|
+
|
|
62
|
+
let links = {};
|
|
63
|
+
if (myrelations) {
|
|
64
|
+
Object.keys(myrelations).forEach((relationtype) => {
|
|
65
|
+
links[relationtype] = {};
|
|
66
|
+
myrelations[relationtype].items.forEach((item) => {
|
|
67
|
+
links[relationtype][item.source.UID] = item.source;
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let links_ordered = {};
|
|
73
|
+
Object.keys(links).forEach((relationtype) => {
|
|
74
|
+
links_ordered[relationtype] = Object.values(links[relationtype]).sort(
|
|
75
|
+
(link) => link['@id'],
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const relations_found = Object.keys(links_ordered).length > 0;
|
|
80
|
+
return (
|
|
81
|
+
<Container id="linkstoitem">
|
|
82
|
+
<Helmet title={intl.formatMessage(messages.linkstoitem)} />
|
|
83
|
+
<Segment.Group raised>
|
|
84
|
+
<Segment className="primary">
|
|
85
|
+
<FormattedMessage
|
|
86
|
+
id="Content that links to or references {title}"
|
|
87
|
+
defaultMessage="Content that links to or references {title}"
|
|
88
|
+
values={{ title: <q>{title}</q> }}
|
|
89
|
+
/>
|
|
90
|
+
</Segment>
|
|
91
|
+
{relations_found ? (
|
|
92
|
+
<Table selectable compact singleLine attached>
|
|
93
|
+
{
|
|
94
|
+
<Table.Body>
|
|
95
|
+
{Object.keys(links_ordered).map((relationtype) => {
|
|
96
|
+
return [].concat(
|
|
97
|
+
[
|
|
98
|
+
<Table.Row>
|
|
99
|
+
<Table.HeaderCell>
|
|
100
|
+
{relationtype === 'isReferencing' ? (
|
|
101
|
+
<FormattedMessage
|
|
102
|
+
id="Linking this item with hyperlink in text"
|
|
103
|
+
defaultMessage="Linking this item with hyperlink in text"
|
|
104
|
+
/>
|
|
105
|
+
) : relationtype === 'relatedItems' ? (
|
|
106
|
+
<FormattedMessage
|
|
107
|
+
id="Referencing this item as related item"
|
|
108
|
+
defaultMessage="Referencing this item as related item"
|
|
109
|
+
/>
|
|
110
|
+
) : (
|
|
111
|
+
<>
|
|
112
|
+
<FormattedMessage
|
|
113
|
+
id="Referencing this item with {relationship}"
|
|
114
|
+
defaultMessage="Referencing this item with {relationship}"
|
|
115
|
+
values={{ relationship: <q>{relationtype}</q> }}
|
|
116
|
+
/>
|
|
117
|
+
</>
|
|
118
|
+
)}
|
|
119
|
+
</Table.HeaderCell>
|
|
120
|
+
<Table.HeaderCell>
|
|
121
|
+
<FormattedMessage
|
|
122
|
+
id="Review state"
|
|
123
|
+
defaultMessage="Review state"
|
|
124
|
+
/>
|
|
125
|
+
</Table.HeaderCell>
|
|
126
|
+
<Table.HeaderCell>
|
|
127
|
+
<FormattedMessage id="Type" defaultMessage="Type" />
|
|
128
|
+
</Table.HeaderCell>
|
|
129
|
+
</Table.Row>,
|
|
130
|
+
],
|
|
131
|
+
links_ordered[relationtype].map((link) => {
|
|
132
|
+
return (
|
|
133
|
+
<Table.Row key={link['@id']}>
|
|
134
|
+
<Table.Cell>
|
|
135
|
+
<UniversalLink
|
|
136
|
+
href={link['@id']}
|
|
137
|
+
className={`source ${link.review_state}`}
|
|
138
|
+
>
|
|
139
|
+
<span className="label" title={link.type_title}>
|
|
140
|
+
{link.title}
|
|
141
|
+
</span>
|
|
142
|
+
</UniversalLink>
|
|
143
|
+
</Table.Cell>
|
|
144
|
+
<Table.Cell>
|
|
145
|
+
<span>{link.review_state}</span>
|
|
146
|
+
</Table.Cell>
|
|
147
|
+
<Table.Cell>
|
|
148
|
+
<span>{link.type_title || ''}</span>
|
|
149
|
+
</Table.Cell>
|
|
150
|
+
</Table.Row>
|
|
151
|
+
);
|
|
152
|
+
}),
|
|
153
|
+
);
|
|
154
|
+
})}
|
|
155
|
+
</Table.Body>
|
|
156
|
+
}
|
|
157
|
+
</Table>
|
|
158
|
+
) : (
|
|
159
|
+
<Segment secondary>
|
|
160
|
+
<FormattedMessage
|
|
161
|
+
id="No links to this item found."
|
|
162
|
+
defaultMessage="No links to this item found."
|
|
163
|
+
/>
|
|
164
|
+
</Segment>
|
|
165
|
+
)}
|
|
166
|
+
</Segment.Group>
|
|
167
|
+
{__CLIENT__ && (
|
|
168
|
+
<Portal node={document.getElementById('toolbar')}>
|
|
169
|
+
<Toolbar
|
|
170
|
+
pathname={pathname}
|
|
171
|
+
hideDefaultViewButtons
|
|
172
|
+
inner={
|
|
173
|
+
<>
|
|
174
|
+
<Link to={itempath} className="item">
|
|
175
|
+
<IconNext
|
|
176
|
+
name={backSVG}
|
|
177
|
+
className="contents circled"
|
|
178
|
+
size="30px"
|
|
179
|
+
title={intl.formatMessage(messages.back)}
|
|
180
|
+
/>
|
|
181
|
+
</Link>
|
|
182
|
+
|
|
183
|
+
<>
|
|
184
|
+
{ploneSetupAction ? (
|
|
185
|
+
<Link to="/controlpanel/relations" className="relations">
|
|
186
|
+
<IconNext
|
|
187
|
+
name={settingsSVG}
|
|
188
|
+
className="circled"
|
|
189
|
+
aria-label={intl.formatMessage(
|
|
190
|
+
messages.helpLinkRelationsControlPanel,
|
|
191
|
+
)}
|
|
192
|
+
size="30px"
|
|
193
|
+
title={intl.formatMessage(
|
|
194
|
+
messages.helpLinkRelationsControlPanel,
|
|
195
|
+
)}
|
|
196
|
+
/>
|
|
197
|
+
</Link>
|
|
198
|
+
) : null}
|
|
199
|
+
</>
|
|
200
|
+
</>
|
|
201
|
+
}
|
|
202
|
+
/>
|
|
203
|
+
</Portal>
|
|
204
|
+
)}
|
|
205
|
+
</Container>
|
|
206
|
+
);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export default LinksToItem;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer from 'react-test-renderer';
|
|
3
|
+
import { Provider } from 'react-intl-redux';
|
|
4
|
+
import configureMockStore from 'redux-mock-store';
|
|
5
|
+
import thunk from 'redux-thunk';
|
|
6
|
+
|
|
7
|
+
import LinksToItem from './LinksToItem';
|
|
8
|
+
|
|
9
|
+
const middlewares = [thunk];
|
|
10
|
+
const mockStore = configureMockStore(middlewares);
|
|
11
|
+
|
|
12
|
+
jest.mock('react-portal', () => ({
|
|
13
|
+
Portal: jest.fn(() => <div id="Portal" />),
|
|
14
|
+
}));
|
|
15
|
+
jest.mock('../Toolbar/More', () => jest.fn(() => <div className="More" />));
|
|
16
|
+
|
|
17
|
+
describe('LinksToItem', () => {
|
|
18
|
+
it('renders "links and references" view', () => {
|
|
19
|
+
const store = mockStore({
|
|
20
|
+
relations: {
|
|
21
|
+
subrequests: {
|
|
22
|
+
'/page-1': {
|
|
23
|
+
relations: {
|
|
24
|
+
isReferencing: {
|
|
25
|
+
items: [
|
|
26
|
+
{
|
|
27
|
+
source: {
|
|
28
|
+
'@id': 'http://localhost:3000/page-basil',
|
|
29
|
+
'@type': 'Document',
|
|
30
|
+
UID: 'SOMEUID008',
|
|
31
|
+
description: '',
|
|
32
|
+
review_state: 'published',
|
|
33
|
+
title: 'Basil',
|
|
34
|
+
type_title: 'Document',
|
|
35
|
+
},
|
|
36
|
+
target: {
|
|
37
|
+
'@id': 'http://localhost:3000/page-tomato',
|
|
38
|
+
'@type': 'Document',
|
|
39
|
+
UID: 'SOMEUID007',
|
|
40
|
+
description: '',
|
|
41
|
+
review_state: 'published',
|
|
42
|
+
title: 'Tomato',
|
|
43
|
+
type_title: 'Document',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
items_total: 1,
|
|
48
|
+
},
|
|
49
|
+
relatedItems: {
|
|
50
|
+
items: [
|
|
51
|
+
{
|
|
52
|
+
source: {
|
|
53
|
+
'@id': 'http://localhost:3000/page-cucumber',
|
|
54
|
+
'@type': 'Document',
|
|
55
|
+
UID: 'SOMEUID008',
|
|
56
|
+
description: '',
|
|
57
|
+
review_state: 'published',
|
|
58
|
+
title: 'Cucumber',
|
|
59
|
+
type_title: 'Document',
|
|
60
|
+
},
|
|
61
|
+
target: {
|
|
62
|
+
'@id': 'http://localhost:3000/page-tomato',
|
|
63
|
+
'@type': 'Document',
|
|
64
|
+
UID: 'SOMEUID007',
|
|
65
|
+
description: '',
|
|
66
|
+
review_state: 'published',
|
|
67
|
+
title: 'Tomato',
|
|
68
|
+
type_title: 'Document',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
items_total: 1,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
content: {
|
|
79
|
+
data: {
|
|
80
|
+
UID: 'SOMEUID007',
|
|
81
|
+
title: 'page #1',
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
intl: {
|
|
85
|
+
locale: 'en',
|
|
86
|
+
messages: {},
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
const component = renderer.create(
|
|
90
|
+
<Provider store={store}>
|
|
91
|
+
<LinksToItem location={{ pathname: '/page-1/links-to-item' }} />
|
|
92
|
+
</Provider>,
|
|
93
|
+
);
|
|
94
|
+
const json = component.toJSON();
|
|
95
|
+
expect(json).toMatchSnapshot();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -53,6 +53,10 @@ const messages = defineMessages({
|
|
|
53
53
|
id: 'URL Management',
|
|
54
54
|
defaultMessage: 'URL Management',
|
|
55
55
|
},
|
|
56
|
+
linkstoitem: {
|
|
57
|
+
id: 'Links and references',
|
|
58
|
+
defaultMessage: 'Links and references',
|
|
59
|
+
},
|
|
56
60
|
ManageTranslations: {
|
|
57
61
|
id: 'Manage Translations',
|
|
58
62
|
defaultMessage: 'Manage Translations',
|
|
@@ -227,6 +231,7 @@ class More extends Component {
|
|
|
227
231
|
const aliasesAction = find(this.props.actions.object_buttons, {
|
|
228
232
|
id: 'redirection',
|
|
229
233
|
});
|
|
234
|
+
|
|
230
235
|
const { content, intl } = this.props;
|
|
231
236
|
|
|
232
237
|
const dateOptions = {
|
|
@@ -317,6 +322,16 @@ class More extends Component {
|
|
|
317
322
|
</li>
|
|
318
323
|
)}
|
|
319
324
|
</Plug>
|
|
325
|
+
{path !== '' && !config.settings.excludeLinksAndReferencesMenuItem && (
|
|
326
|
+
<Plug pluggable="toolbar-more-menu-list" id="linkstoitems">
|
|
327
|
+
<li>
|
|
328
|
+
<Link to={`${path}/links-to-item`}>
|
|
329
|
+
{this.props.intl.formatMessage(messages.linkstoitem)}
|
|
330
|
+
<Icon name={rightArrowSVG} size="24px" />
|
|
331
|
+
</Link>
|
|
332
|
+
</li>
|
|
333
|
+
</Plug>
|
|
334
|
+
)}
|
|
320
335
|
<Plug pluggable="toolbar-more-menu-list" id="rules">
|
|
321
336
|
{rulesAction && (
|
|
322
337
|
<li>
|
|
@@ -1,20 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
* Breadcrumbs components.
|
|
3
|
-
* @module components/theme/Breadcrumbs/Breadcrumbs
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React, { Component } from 'react';
|
|
1
|
+
import { useEffect } from 'react';
|
|
7
2
|
import PropTypes from 'prop-types';
|
|
8
|
-
import { connect } from 'react-redux';
|
|
9
|
-
import { compose } from 'redux';
|
|
10
3
|
import { Link } from 'react-router-dom';
|
|
11
4
|
import { Breadcrumb, Container, Segment } from 'semantic-ui-react';
|
|
12
|
-
import { defineMessages,
|
|
5
|
+
import { defineMessages, useIntl } from 'react-intl';
|
|
6
|
+
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
|
|
13
7
|
|
|
14
|
-
import { Icon } from '@plone/volto/components';
|
|
15
8
|
import { getBreadcrumbs } from '@plone/volto/actions';
|
|
16
9
|
import { getBaseUrl, hasApiExpander } from '@plone/volto/helpers';
|
|
17
|
-
|
|
10
|
+
import { Icon } from '@plone/volto/components';
|
|
18
11
|
import homeSVG from '@plone/volto/icons/home.svg';
|
|
19
12
|
|
|
20
13
|
const messages = defineMessages({
|
|
@@ -28,96 +21,56 @@ const messages = defineMessages({
|
|
|
28
21
|
},
|
|
29
22
|
});
|
|
30
23
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
export class BreadcrumbsComponent extends Component {
|
|
35
|
-
/**
|
|
36
|
-
* Property types.
|
|
37
|
-
* @property {Object} propTypes Property types.
|
|
38
|
-
* @static
|
|
39
|
-
*/
|
|
40
|
-
static propTypes = {
|
|
41
|
-
getBreadcrumbs: PropTypes.func.isRequired,
|
|
42
|
-
pathname: PropTypes.string.isRequired,
|
|
43
|
-
root: PropTypes.string,
|
|
44
|
-
items: PropTypes.arrayOf(
|
|
45
|
-
PropTypes.shape({
|
|
46
|
-
title: PropTypes.string,
|
|
47
|
-
url: PropTypes.string,
|
|
48
|
-
}),
|
|
49
|
-
).isRequired,
|
|
50
|
-
};
|
|
24
|
+
const BreadcrumbsComponent = ({ pathname }) => {
|
|
25
|
+
const intl = useIntl();
|
|
26
|
+
const dispatch = useDispatch();
|
|
51
27
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.props.getBreadcrumbs(getBaseUrl(this.props.pathname));
|
|
55
|
-
}
|
|
56
|
-
}
|
|
28
|
+
const items = useSelector((state) => state.breadcrumbs.items, shallowEqual);
|
|
29
|
+
const root = useSelector((state) => state.breadcrumbs.root);
|
|
57
30
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
* @param {Object} nextProps Next properties
|
|
62
|
-
* @returns {undefined}
|
|
63
|
-
*/
|
|
64
|
-
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
65
|
-
if (nextProps.pathname !== this.props.pathname) {
|
|
66
|
-
if (!hasApiExpander('breadcrumbs', getBaseUrl(this.props.pathname))) {
|
|
67
|
-
this.props.getBreadcrumbs(getBaseUrl(nextProps.pathname));
|
|
68
|
-
}
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!hasApiExpander('breadcrumbs', getBaseUrl(pathname))) {
|
|
33
|
+
dispatch(getBreadcrumbs(getBaseUrl(pathname)));
|
|
69
34
|
}
|
|
70
|
-
}
|
|
35
|
+
}, [dispatch, pathname]);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Segment
|
|
39
|
+
role="navigation"
|
|
40
|
+
aria-label={intl.formatMessage(messages.breadcrumbs)}
|
|
41
|
+
className="breadcrumbs"
|
|
42
|
+
secondary
|
|
43
|
+
vertical
|
|
44
|
+
>
|
|
45
|
+
<Container>
|
|
46
|
+
<Breadcrumb>
|
|
47
|
+
<Link
|
|
48
|
+
to={root || '/'}
|
|
49
|
+
className="section"
|
|
50
|
+
title={intl.formatMessage(messages.home)}
|
|
51
|
+
>
|
|
52
|
+
<Icon name={homeSVG} size="18px" />
|
|
53
|
+
</Link>
|
|
54
|
+
{items.map((item, index, items) => [
|
|
55
|
+
<Breadcrumb.Divider key={`divider-${item.url}`} />,
|
|
56
|
+
index < items.length - 1 ? (
|
|
57
|
+
<Link key={item.url} to={item.url} className="section">
|
|
58
|
+
{item.title}
|
|
59
|
+
</Link>
|
|
60
|
+
) : (
|
|
61
|
+
<Breadcrumb.Section key={item.url} active>
|
|
62
|
+
{item.title}
|
|
63
|
+
</Breadcrumb.Section>
|
|
64
|
+
),
|
|
65
|
+
])}
|
|
66
|
+
</Breadcrumb>
|
|
67
|
+
</Container>
|
|
68
|
+
</Segment>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
* @returns {string} Markup for the component.
|
|
76
|
-
*/
|
|
77
|
-
render() {
|
|
78
|
-
return (
|
|
79
|
-
<Segment
|
|
80
|
-
role="navigation"
|
|
81
|
-
aria-label={this.props.intl.formatMessage(messages.breadcrumbs)}
|
|
82
|
-
className="breadcrumbs"
|
|
83
|
-
secondary
|
|
84
|
-
vertical
|
|
85
|
-
>
|
|
86
|
-
<Container>
|
|
87
|
-
<Breadcrumb>
|
|
88
|
-
<Link
|
|
89
|
-
to={this.props.root || '/'}
|
|
90
|
-
className="section"
|
|
91
|
-
title={this.props.intl.formatMessage(messages.home)}
|
|
92
|
-
>
|
|
93
|
-
<Icon name={homeSVG} size="18px" />
|
|
94
|
-
</Link>
|
|
95
|
-
{this.props.items.map((item, index, items) => [
|
|
96
|
-
<Breadcrumb.Divider key={`divider-${item.url}`} />,
|
|
97
|
-
index < items.length - 1 ? (
|
|
98
|
-
<Link key={item.url} to={item.url} className="section">
|
|
99
|
-
{item.title}
|
|
100
|
-
</Link>
|
|
101
|
-
) : (
|
|
102
|
-
<Breadcrumb.Section key={item.url} active>
|
|
103
|
-
{item.title}
|
|
104
|
-
</Breadcrumb.Section>
|
|
105
|
-
),
|
|
106
|
-
])}
|
|
107
|
-
</Breadcrumb>
|
|
108
|
-
</Container>
|
|
109
|
-
</Segment>
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
72
|
+
BreadcrumbsComponent.propTypes = {
|
|
73
|
+
pathname: PropTypes.string.isRequired,
|
|
74
|
+
};
|
|
113
75
|
|
|
114
|
-
export default
|
|
115
|
-
injectIntl,
|
|
116
|
-
connect(
|
|
117
|
-
(state) => ({
|
|
118
|
-
items: state.breadcrumbs.items,
|
|
119
|
-
root: state.breadcrumbs.root,
|
|
120
|
-
}),
|
|
121
|
-
{ getBreadcrumbs },
|
|
122
|
-
),
|
|
123
|
-
)(BreadcrumbsComponent);
|
|
76
|
+
export default BreadcrumbsComponent;
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { injectIntl } from 'react-intl';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import
|
|
3
|
+
import BreadcrumbsComponent from './Breadcrumbs';
|
|
4
4
|
import Wrapper from '@plone/volto/storybook';
|
|
5
5
|
|
|
6
6
|
export const Breadcrumb = injectIntl(({ children, ...args }) => {
|
|
7
7
|
return (
|
|
8
|
-
<Wrapper
|
|
8
|
+
<Wrapper
|
|
9
|
+
anonymous
|
|
10
|
+
location={{ pathname: '/folder2/folder21/doc212' }}
|
|
11
|
+
customStore={{
|
|
12
|
+
breadcrumbs: {
|
|
13
|
+
items: [
|
|
14
|
+
{ title: 'Blog', url: '/blog' },
|
|
15
|
+
{ title: 'My first blog', url: '/blog/my-first-blog' },
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
}}
|
|
19
|
+
>
|
|
9
20
|
<div className="ui segment form attached" style={{ width: '400px' }}>
|
|
10
|
-
<BreadcrumbsComponent
|
|
11
|
-
pathname=""
|
|
12
|
-
items={[
|
|
13
|
-
{
|
|
14
|
-
'@id': 'https://volto.kitconcept.com/api/Members',
|
|
15
|
-
title: 'Users',
|
|
16
|
-
},
|
|
17
|
-
]}
|
|
18
|
-
getBreadcrumbs={() => {}}
|
|
19
|
-
{...args}
|
|
20
|
-
/>
|
|
21
|
+
<BreadcrumbsComponent pathname="" />
|
|
21
22
|
</div>
|
|
22
23
|
</Wrapper>
|
|
23
24
|
);
|