@eeacms/volto-eea-website-theme 3.7.0 → 3.9.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 +19 -2
- package/package.json +3 -1
- package/src/actions/index.js +1 -0
- package/src/actions/navigation.js +24 -0
- package/src/actions/print.js +9 -1
- package/src/components/manage/Blocks/ContextNavigation/variations/Accordion.jsx +42 -35
- package/src/components/manage/Blocks/LayoutSettings/LayoutSettingsEdit.test.jsx +383 -0
- package/src/components/manage/Blocks/Title/variations/WebReportPage.test.jsx +232 -0
- package/src/components/theme/Banner/View.jsx +11 -92
- package/src/components/theme/PrintLoader/PrintLoader.jsx +56 -0
- package/src/components/theme/PrintLoader/PrintLoader.test.jsx +91 -0
- package/src/components/theme/PrintLoader/style.less +12 -0
- package/src/components/theme/WebReport/WebReportSectionView.test.jsx +462 -0
- package/src/components/theme/Widgets/ImageViewWidget.test.jsx +26 -0
- package/src/components/theme/Widgets/NavigationBehaviorWidget.jsx +601 -0
- package/src/components/theme/Widgets/NavigationBehaviorWidget.test.jsx +507 -0
- package/src/components/theme/Widgets/SimpleArrayWidget.jsx +183 -0
- package/src/components/theme/Widgets/SimpleArrayWidget.test.jsx +283 -0
- package/src/constants/ActionTypes.js +2 -0
- package/src/customizations/volto/components/manage/History/History.diff +207 -0
- package/src/customizations/volto/components/manage/History/History.jsx +444 -0
- package/src/customizations/volto/components/theme/Comments/Comments.jsx +9 -2
- package/src/customizations/volto/components/theme/Comments/Comments.test.jsx +4 -4
- package/src/customizations/volto/components/theme/Comments/comments.less +16 -0
- package/src/customizations/volto/components/theme/Header/Header.jsx +60 -1
- package/src/customizations/volto/components/theme/View/DefaultView.jsx +42 -33
- package/src/customizations/volto/helpers/Html/Html.jsx +212 -0
- package/src/customizations/volto/helpers/Html/Readme.md +1 -0
- package/src/customizations/volto/server.jsx +375 -0
- package/src/helpers/loadLazyImages.js +11 -0
- package/src/helpers/loadLazyImages.test.js +22 -0
- package/src/helpers/setupPrintView.js +134 -0
- package/src/helpers/setupPrintView.test.js +49 -0
- package/src/index.js +11 -1
- package/src/index.test.js +6 -0
- package/src/middleware/voltoCustom.test.js +282 -0
- package/src/reducers/index.js +2 -1
- package/src/reducers/navigation/navigation.js +47 -0
- package/src/reducers/navigation/navigation.test.js +348 -0
- package/src/reducers/navigation.js +55 -0
- package/src/reducers/print.js +18 -8
- package/src/reducers/print.test.js +117 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import { Provider } from 'react-intl-redux';
|
|
4
|
+
import configureStore from 'redux-mock-store';
|
|
5
|
+
import SimpleArrayWidget from './SimpleArrayWidget';
|
|
6
|
+
|
|
7
|
+
// Add jest-dom matchers
|
|
8
|
+
import '@testing-library/jest-dom';
|
|
9
|
+
|
|
10
|
+
const mockStore = configureStore();
|
|
11
|
+
|
|
12
|
+
describe('SimpleArrayWidget', () => {
|
|
13
|
+
let store;
|
|
14
|
+
const mockOnChange = jest.fn();
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
store = mockStore({
|
|
18
|
+
intl: {
|
|
19
|
+
locale: 'en',
|
|
20
|
+
messages: {
|
|
21
|
+
Add: 'Add',
|
|
22
|
+
Remove: 'Remove',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
mockOnChange.mockClear();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const defaultProps = {
|
|
30
|
+
id: 'test-field',
|
|
31
|
+
title: 'Test Field',
|
|
32
|
+
description: 'Test description',
|
|
33
|
+
value: [],
|
|
34
|
+
onChange: mockOnChange,
|
|
35
|
+
items: {
|
|
36
|
+
minimum: 1,
|
|
37
|
+
maximum: 5,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
it('renders without crashing', () => {
|
|
42
|
+
const { container } = render(
|
|
43
|
+
<Provider store={store}>
|
|
44
|
+
<SimpleArrayWidget {...defaultProps} />
|
|
45
|
+
</Provider>,
|
|
46
|
+
);
|
|
47
|
+
expect(container).toBeTruthy();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('displays the title', () => {
|
|
51
|
+
render(
|
|
52
|
+
<Provider store={store}>
|
|
53
|
+
<SimpleArrayWidget {...defaultProps} />
|
|
54
|
+
</Provider>,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
expect(screen.getByText('Test Field')).toBeInTheDocument();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('shows Add button when no input is shown', () => {
|
|
61
|
+
render(
|
|
62
|
+
<Provider store={store}>
|
|
63
|
+
<SimpleArrayWidget {...defaultProps} />
|
|
64
|
+
</Provider>,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(screen.getByRole('button', { name: /add/i })).toBeInTheDocument();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('shows input field when Add button is clicked', () => {
|
|
71
|
+
render(
|
|
72
|
+
<Provider store={store}>
|
|
73
|
+
<SimpleArrayWidget {...defaultProps} />
|
|
74
|
+
</Provider>,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
fireEvent.click(screen.getByRole('button', { name: /add/i }));
|
|
78
|
+
|
|
79
|
+
expect(screen.getByRole('spinbutton')).toBeInTheDocument();
|
|
80
|
+
expect(screen.getByTitle('Add')).toBeInTheDocument();
|
|
81
|
+
expect(screen.getByTitle('Cancel')).toBeInTheDocument();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('adds a valid number to the array', () => {
|
|
85
|
+
render(
|
|
86
|
+
<Provider store={store}>
|
|
87
|
+
<SimpleArrayWidget {...defaultProps} />
|
|
88
|
+
</Provider>,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
fireEvent.click(screen.getByRole('button', { name: /add/i }));
|
|
92
|
+
|
|
93
|
+
const input = screen.getByRole('spinbutton');
|
|
94
|
+
fireEvent.change(input, { target: { value: '3' } });
|
|
95
|
+
fireEvent.click(screen.getByTitle('Add'));
|
|
96
|
+
|
|
97
|
+
expect(mockOnChange).toHaveBeenCalledWith('test-field', [3]);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('does not add invalid numbers', () => {
|
|
101
|
+
render(
|
|
102
|
+
<Provider store={store}>
|
|
103
|
+
<SimpleArrayWidget {...defaultProps} />
|
|
104
|
+
</Provider>,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
fireEvent.click(screen.getByRole('button', { name: /add/i }));
|
|
108
|
+
|
|
109
|
+
const input = screen.getByRole('spinbutton');
|
|
110
|
+
fireEvent.change(input, { target: { value: '10' } }); // Above maximum
|
|
111
|
+
fireEvent.click(screen.getByTitle('Add'));
|
|
112
|
+
|
|
113
|
+
expect(mockOnChange).not.toHaveBeenCalled();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('cancels input when cancel button is clicked', () => {
|
|
117
|
+
render(
|
|
118
|
+
<Provider store={store}>
|
|
119
|
+
<SimpleArrayWidget {...defaultProps} />
|
|
120
|
+
</Provider>,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
fireEvent.click(screen.getByRole('button', { name: /add/i }));
|
|
124
|
+
|
|
125
|
+
const input = screen.getByRole('spinbutton');
|
|
126
|
+
fireEvent.change(input, { target: { value: '2' } });
|
|
127
|
+
fireEvent.click(screen.getByTitle('Cancel'));
|
|
128
|
+
|
|
129
|
+
// Input should be hidden after cancel
|
|
130
|
+
expect(screen.queryByRole('spinbutton')).not.toBeInTheDocument();
|
|
131
|
+
expect(mockOnChange).not.toHaveBeenCalled();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('displays existing values as labels', () => {
|
|
135
|
+
const props = {
|
|
136
|
+
...defaultProps,
|
|
137
|
+
value: [1, 3, 5],
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
render(
|
|
141
|
+
<Provider store={store}>
|
|
142
|
+
<SimpleArrayWidget {...props} />
|
|
143
|
+
</Provider>,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
expect(screen.getByText('1')).toBeInTheDocument();
|
|
147
|
+
expect(screen.getByText('3')).toBeInTheDocument();
|
|
148
|
+
expect(screen.getByText('5')).toBeInTheDocument();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('removes value when × is clicked', () => {
|
|
152
|
+
const props = {
|
|
153
|
+
...defaultProps,
|
|
154
|
+
value: [1, 3, 5],
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
render(
|
|
158
|
+
<Provider store={store}>
|
|
159
|
+
<SimpleArrayWidget {...props} />
|
|
160
|
+
</Provider>,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
const removeButtons = screen.getAllByText('×');
|
|
164
|
+
fireEvent.click(removeButtons[1]); // Remove second item (value 3)
|
|
165
|
+
|
|
166
|
+
expect(mockOnChange).toHaveBeenCalledWith('test-field', [1, 5]);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('passes null when removing last item', () => {
|
|
170
|
+
const props = {
|
|
171
|
+
...defaultProps,
|
|
172
|
+
value: [1],
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
render(
|
|
176
|
+
<Provider store={store}>
|
|
177
|
+
<SimpleArrayWidget {...props} />
|
|
178
|
+
</Provider>,
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const removeButton = screen.getByText('×');
|
|
182
|
+
fireEvent.click(removeButton);
|
|
183
|
+
|
|
184
|
+
expect(mockOnChange).toHaveBeenCalledWith('test-field', null);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('handles non-array value gracefully', () => {
|
|
188
|
+
const props = {
|
|
189
|
+
...defaultProps,
|
|
190
|
+
value: null,
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const { container } = render(
|
|
194
|
+
<Provider store={store}>
|
|
195
|
+
<SimpleArrayWidget {...props} />
|
|
196
|
+
</Provider>,
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
expect(container).toBeTruthy();
|
|
200
|
+
expect(screen.getByRole('button', { name: /add/i })).toBeInTheDocument();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('displays numbers correctly', () => {
|
|
204
|
+
const props = {
|
|
205
|
+
...defaultProps,
|
|
206
|
+
value: [1, 2, 3],
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
render(
|
|
210
|
+
<Provider store={store}>
|
|
211
|
+
<SimpleArrayWidget {...props} />
|
|
212
|
+
</Provider>,
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
expect(screen.getByText('1')).toBeInTheDocument();
|
|
216
|
+
expect(screen.getByText('2')).toBeInTheDocument();
|
|
217
|
+
expect(screen.getByText('3')).toBeInTheDocument();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('uses default min/max values when not provided', () => {
|
|
221
|
+
const props = {
|
|
222
|
+
...defaultProps,
|
|
223
|
+
items: undefined,
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
render(
|
|
227
|
+
<Provider store={store}>
|
|
228
|
+
<SimpleArrayWidget {...props} />
|
|
229
|
+
</Provider>,
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
fireEvent.click(screen.getByRole('button', { name: /add/i }));
|
|
233
|
+
|
|
234
|
+
const input = screen.getByRole('spinbutton');
|
|
235
|
+
expect(input).toHaveAttribute('min', '1');
|
|
236
|
+
expect(input).toHaveAttribute('max', '10');
|
|
237
|
+
expect(input).toHaveAttribute('placeholder', '1-10');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('uses custom min/max values when provided', () => {
|
|
241
|
+
render(
|
|
242
|
+
<Provider store={store}>
|
|
243
|
+
<SimpleArrayWidget {...defaultProps} />
|
|
244
|
+
</Provider>,
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
fireEvent.click(screen.getByRole('button', { name: /add/i }));
|
|
248
|
+
|
|
249
|
+
const input = screen.getByRole('spinbutton');
|
|
250
|
+
expect(input).toHaveAttribute('min', '1');
|
|
251
|
+
expect(input).toHaveAttribute('max', '5');
|
|
252
|
+
expect(input).toHaveAttribute('placeholder', '1-5');
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('disables add button when input is empty', () => {
|
|
256
|
+
render(
|
|
257
|
+
<Provider store={store}>
|
|
258
|
+
<SimpleArrayWidget {...defaultProps} />
|
|
259
|
+
</Provider>,
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
fireEvent.click(screen.getByRole('button', { name: /add/i }));
|
|
263
|
+
|
|
264
|
+
const addButton = screen.getByTitle('Add');
|
|
265
|
+
expect(addButton).toBeDisabled();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('does not add empty or whitespace-only values', () => {
|
|
269
|
+
render(
|
|
270
|
+
<Provider store={store}>
|
|
271
|
+
<SimpleArrayWidget {...defaultProps} />
|
|
272
|
+
</Provider>,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
fireEvent.click(screen.getByRole('button', { name: /add/i }));
|
|
276
|
+
|
|
277
|
+
const input = screen.getByRole('spinbutton');
|
|
278
|
+
fireEvent.change(input, { target: { value: ' ' } });
|
|
279
|
+
fireEvent.click(screen.getByTitle('Add'));
|
|
280
|
+
|
|
281
|
+
expect(mockOnChange).not.toHaveBeenCalled();
|
|
282
|
+
});
|
|
283
|
+
});
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
--- History.jsx.original 2025-08-11 09:00:00.000000000 +0000
|
|
2
|
+
+++ History.jsx.modified 2025-08-11 09:30:00.000000000 +0000
|
|
3
|
+
@@ -1,6 +1,8 @@
|
|
4
|
+
/**
|
|
5
|
+
* History component.
|
|
6
|
+
* @module components/manage/History/History
|
|
7
|
+
+ * Customized to enable complex versioning
|
|
8
|
+
+ * https://taskman.eionet.europa.eu/issues/289335?issue_count=4&issue_position=1&next_issue_id=285635
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React, { Component } from 'react';
|
|
12
|
+
@@ -13,9 +15,11 @@
|
|
13
|
+
Container as SemanticContainer,
|
|
14
|
+
Dropdown,
|
|
15
|
+
Icon,
|
|
16
|
+
+ Message,
|
|
17
|
+
Segment,
|
|
18
|
+
Table,
|
|
19
|
+
} from 'semantic-ui-react';
|
|
20
|
+
-import { concat, map, reverse, find } from 'lodash';
|
|
21
|
+
+import concat from 'lodash/concat';
|
|
22
|
+
+import map from 'lodash/map';
|
|
23
|
+
+import reverse from 'lodash/reverse';
|
|
24
|
+
+import find from 'lodash/find';
|
|
25
|
+
-import { Portal } from 'react-portal';
|
|
26
|
+
+import { createPortal } from 'react-dom';
|
|
27
|
+
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
|
28
|
+
-import { asyncConnect } from '@plone/volto/helpers';
|
|
29
|
+
+import { asyncConnect } from '@plone/volto/helpers/AsyncConnect';
|
|
30
|
+
|
|
31
|
+
-import {
|
|
32
|
+
- FormattedDate,
|
|
33
|
+
- Icon as IconNext,
|
|
34
|
+
- Toolbar,
|
|
35
|
+
- Forbidden,
|
|
36
|
+
- Unauthorized,
|
|
37
|
+
-} from '@plone/volto/components';
|
|
38
|
+
-import { getHistory, revertHistory, listActions } from '@plone/volto/actions';
|
|
39
|
+
+import FormattedDate from '@plone/volto/components/theme/FormattedDate/FormattedDate';
|
|
40
|
+
+import IconNext from '@plone/volto/components/theme/Icon/Icon';
|
|
41
|
+
+import Toolbar from '@plone/volto/components/manage/Toolbar/Toolbar';
|
|
42
|
+
+import Forbidden from '@plone/volto/components/theme/Forbidden/Forbidden';
|
|
43
|
+
+import Unauthorized from '@plone/volto/components/theme/Unauthorized/Unauthorized';
|
|
44
|
+
+import {
|
|
45
|
+
+ getHistory,
|
|
46
|
+
+ revertHistory,
|
|
47
|
+
+} from '@plone/volto/actions/history/history';
|
|
48
|
+
+import { listActions } from '@plone/volto/actions/actions/actions';
|
|
49
|
+
import { getBaseUrl } from '@plone/volto/helpers';
|
|
50
|
+
import config from '@plone/volto/registry';
|
|
51
|
+
|
|
52
|
+
@@ -34,6 +38,22 @@
|
|
53
|
+
history: {
|
|
54
|
+
id: 'History',
|
|
55
|
+
defaultMessage: 'History',
|
|
56
|
+
},
|
|
57
|
+
+ newerVersionAvailable: {
|
|
58
|
+
+ id: 'Newer version available',
|
|
59
|
+
+ defaultMessage: 'Newer version available',
|
|
60
|
+
+ },
|
|
61
|
+
+ thereIsNewerVersionAt: {
|
|
62
|
+
+ id: 'There is a newer version at {link}',
|
|
63
|
+
+ defaultMessage: 'There is a newer version at {link}',
|
|
64
|
+
+ },
|
|
65
|
+
+ olderVersionAvailable: {
|
|
66
|
+
+ id: 'Older version available',
|
|
67
|
+
+ defaultMessage: 'Older version available',
|
|
68
|
+
+ },
|
|
69
|
+
+ thereIsOlderVersionAt: {
|
|
70
|
+
+ id: 'There is an older version at {link}',
|
|
71
|
+
+ defaultMessage: 'There is an older version at {link}',
|
|
72
|
+
+ },
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
@@ -138,6 +158,25 @@
|
|
77
|
+
/>
|
|
78
|
+
</Segment>
|
|
79
|
+
+ {this.props.content?.copied_to && (
|
|
80
|
+
+ <Message info icon attached="top">
|
|
81
|
+
+ <Icon name="arrow right" />
|
|
82
|
+
+ <Message.Content>
|
|
83
|
+
+ <Message.Header>
|
|
84
|
+
+ <FormattedMessage {...messages.newerVersionAvailable} />
|
|
85
|
+
+ </Message.Header>
|
|
86
|
+
+ <FormattedMessage
|
|
87
|
+
+ {...messages.thereIsNewerVersionAt}
|
|
88
|
+
+ values={{
|
|
89
|
+
+ link: (
|
|
90
|
+
+ <Link to={new URL(this.props.content.copied_to).pathname}>
|
|
91
|
+
+ {new URL(this.props.content.copied_to).pathname
|
|
92
|
+
+ .split('/')
|
|
93
|
+
+ .pop() || 'newer version'}
|
|
94
|
+
+ </Link>
|
|
95
|
+
+ ),
|
|
96
|
+
+ }}
|
|
97
|
+
+ />
|
|
98
|
+
+ </Message.Content>
|
|
99
|
+
+ </Message>
|
|
100
|
+
+ )}
|
|
101
|
+
- <Table selectable compact singleLine attached>
|
|
102
|
+
+ <Table
|
|
103
|
+
+ selectable
|
|
104
|
+
+ compact
|
|
105
|
+
+ attached
|
|
106
|
+
+ style={{
|
|
107
|
+
+ tableLayout: 'fixed',
|
|
108
|
+
+ width: '100%',
|
|
109
|
+
+ wordWrap: 'break-word',
|
|
110
|
+
+ }}
|
|
111
|
+
+ >
|
|
112
|
+
<Table.Header>
|
|
113
|
+
<Table.Row>
|
|
114
|
+
- <Table.HeaderCell width={1}>
|
|
115
|
+
+ <Table.HeaderCell style={{ width: '5%' }}>
|
|
116
|
+
<FormattedMessage
|
|
117
|
+
id="History Version Number"
|
|
118
|
+
defaultMessage="#"
|
|
119
|
+
/>
|
|
120
|
+
</Table.HeaderCell>
|
|
121
|
+
- <Table.HeaderCell width={4}>
|
|
122
|
+
+ <Table.HeaderCell style={{ width: '25%' }}>
|
|
123
|
+
<FormattedMessage id="What" defaultMessage="What" />
|
|
124
|
+
</Table.HeaderCell>
|
|
125
|
+
- <Table.HeaderCell width={4}>
|
|
126
|
+
+ <Table.HeaderCell style={{ width: '15%' }}>
|
|
127
|
+
<FormattedMessage id="Who" defaultMessage="Who" />
|
|
128
|
+
</Table.HeaderCell>
|
|
129
|
+
- <Table.HeaderCell width={4}>
|
|
130
|
+
+ <Table.HeaderCell style={{ width: '15%' }}>
|
|
131
|
+
<FormattedMessage id="When" defaultMessage="When" />
|
|
132
|
+
</Table.HeaderCell>
|
|
133
|
+
- <Table.HeaderCell width={4}>
|
|
134
|
+
+ <Table.HeaderCell style={{ width: '25%' }}>
|
|
135
|
+
<FormattedMessage
|
|
136
|
+
id="Change Note"
|
|
137
|
+
defaultMessage="Change Note"
|
|
138
|
+
/>
|
|
139
|
+
</Table.HeaderCell>
|
|
140
|
+
- <Table.HeaderCell />
|
|
141
|
+
+ <Table.HeaderCell style={{ width: '15%' }} />
|
|
142
|
+
</Table.Row>
|
|
143
|
+
</Table.Header>
|
|
144
|
+
@@ -280,14 +319,33 @@
|
|
145
|
+
</Table.Body>
|
|
146
|
+
</Table>
|
|
147
|
+
+ {this.props.content?.copied_from && (
|
|
148
|
+
+ <Message warning icon attached="bottom">
|
|
149
|
+
+ <Icon name="arrow left" />
|
|
150
|
+
+ <Message.Content>
|
|
151
|
+
+ <Message.Header>
|
|
152
|
+
+ <FormattedMessage {...messages.olderVersionAvailable} />
|
|
153
|
+
+ </Message.Header>
|
|
154
|
+
+ <FormattedMessage
|
|
155
|
+
+ {...messages.thereIsOlderVersionAt}
|
|
156
|
+
+ values={{
|
|
157
|
+
+ link: (
|
|
158
|
+
+ <Link
|
|
159
|
+
+ to={new URL(this.props.content.copied_from).pathname}
|
|
160
|
+
+ >
|
|
161
|
+
+ {new URL(this.props.content.copied_from).pathname
|
|
162
|
+
+ .split('/')
|
|
163
|
+
+ .pop() || 'older version'}
|
|
164
|
+
+ </Link>
|
|
165
|
+
+ ),
|
|
166
|
+
+ }}
|
|
167
|
+
+ />
|
|
168
|
+
+ </Message.Content>
|
|
169
|
+
+ </Message>
|
|
170
|
+
+ )}
|
|
171
|
+
</Segment.Group>
|
|
172
|
+
- {this.state.isClient && (
|
|
173
|
+
- <Portal node={document.getElementById('toolbar')}>
|
|
174
|
+
+ {this.state.isClient &&
|
|
175
|
+
+ createPortal(
|
|
176
|
+
<Toolbar
|
|
177
|
+
pathname={this.props.pathname}
|
|
178
|
+
hideDefaultViewButtons
|
|
179
|
+
@@ -302,8 +360,9 @@
|
|
180
|
+
</Link>
|
|
181
|
+
}
|
|
182
|
+
- />
|
|
183
|
+
- </Portal>
|
|
184
|
+
- )}
|
|
185
|
+
+ />,
|
|
186
|
+
+ document.getElementById('toolbar'),
|
|
187
|
+
+ )}
|
|
188
|
+
</Container>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
@@ -314,7 +373,7 @@
|
|
192
|
+
asyncConnect([
|
|
193
|
+
{
|
|
194
|
+
key: 'actions',
|
|
195
|
+
- // Dispatch async/await to make the operation syncronous, otherwise it returns
|
|
196
|
+
+ // Dispatch async/await to make the operation synchronous, otherwise it returns
|
|
197
|
+
// before the promise is resolved
|
|
198
|
+
promise: async ({ location, store: { dispatch } }) =>
|
|
199
|
+
await dispatch(listActions(getBaseUrl(location.pathname))),
|
|
200
|
+
@@ -327,6 +386,7 @@
|
|
201
|
+
entries: state.history.entries,
|
|
202
|
+
pathname: props.location.pathname,
|
|
203
|
+
title: state.content.data?.title,
|
|
204
|
+
+ content: state.content.data,
|
|
205
|
+
revertRequest: state.history.revert,
|
|
206
|
+
}),
|
|
207
|
+
{ getHistory, revertHistory },
|