@eeacms/volto-cca-policy 1.0.1 → 1.0.3

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.
Files changed (47) hide show
  1. package/.github/workflows/betterleaks.yml +133 -0
  2. package/.gitleaks.toml +89 -0
  3. package/CHANGELOG.md +262 -1
  4. package/README.md +59 -0
  5. package/RELEASE.md +1 -1
  6. package/jest-addon.config.js +7 -3
  7. package/package.json +3 -2
  8. package/src/components/Search/NavigatorCatalogue/NavigatorCatalogueCardItem.jsx +228 -0
  9. package/src/components/Search/NavigatorCatalogue/NavigatorCatalogueCardItem.test.jsx +167 -0
  10. package/src/components/Search/NavigatorCatalogue/NavigatorCatalogueContentView.jsx +189 -0
  11. package/src/components/Search/NavigatorCatalogue/NavigatorCatalogueMapView.jsx +11 -0
  12. package/src/components/Search/NavigatorCatalogue/utils.js +101 -0
  13. package/src/components/Search/NavigatorCatalogue/utils.test.js +132 -0
  14. package/src/components/Search/NavigatorGuide/NavigatorGuideContentView.jsx +409 -0
  15. package/src/components/Search/NavigatorGuide/NavigatorGuideLayout.jsx +8 -0
  16. package/src/components/index.js +2 -0
  17. package/src/components/manage/Blocks/CollectionStatistics/CollectionStatsView.test.jsx +2 -0
  18. package/src/components/theme/CompareTools/CompareToolsPanel.jsx +151 -0
  19. package/src/components/theme/CompareTools/CompareToolsPanel.test.jsx +96 -0
  20. package/src/components/theme/CompareTools/CompareToolsView.jsx +468 -0
  21. package/src/components/theme/CompareTools/utils.js +111 -0
  22. package/src/components/theme/CompareTools/utils.test.jsx +217 -0
  23. package/src/components/theme/Views/ExtendedToolView.jsx +409 -0
  24. package/src/components/theme/Views/ExtendedToolView.test.jsx +436 -0
  25. package/src/constants.js +1 -0
  26. package/src/customizations/@plone-collective/volto-rss-provider/express-middleware.js +1 -1
  27. package/src/customizations/volto/components/theme/View/LinkView.jsx +1 -1
  28. package/src/customizations/volto/reducers/breadcrumbs/breadcrumbs.js +1 -1
  29. package/src/customizations/volto/server.jsx +1 -1
  30. package/src/helpers/Utils.jsx +29 -0
  31. package/src/helpers/index.js +3 -0
  32. package/src/index.js +31 -2
  33. package/src/search/index.js +6 -0
  34. package/src/search/navigator_catalogue/config.js +89 -0
  35. package/src/search/navigator_catalogue/facets.js +86 -0
  36. package/src/search/navigator_catalogue/views.js +28 -0
  37. package/src/search/navigator_guide/config.js +70 -0
  38. package/src/search/navigator_guide/facets.js +31 -0
  39. package/src/search/navigator_guide/guideSteps.js +93 -0
  40. package/src/slate-styles.less +4 -0
  41. package/src/state.js +1 -0
  42. package/theme/elements/button.overrides +0 -11
  43. package/theme/elements/label.overrides +15 -0
  44. package/theme/globals/blocks.less +3 -2
  45. package/theme/globals/navigator.less +967 -0
  46. package/theme/globals/site.overrides +5 -0
  47. package/theme/tokens/colors.less +2 -0
@@ -0,0 +1,151 @@
1
+ import React from 'react';
2
+ import { useAtom } from 'jotai';
3
+ import { useSelector } from 'react-redux';
4
+ import { useHistory } from 'react-router-dom';
5
+ import { Icon, Button } from 'semantic-ui-react';
6
+ import { defineMessages, useIntl } from 'react-intl';
7
+ import {
8
+ MAX_COMPARE_TOOLS,
9
+ compareToolsAtom,
10
+ getCompareLocation,
11
+ } from './utils';
12
+
13
+ const messages = defineMessages({
14
+ compareTools: {
15
+ id: 'Compare tools',
16
+ defaultMessage: 'Compare tools',
17
+ },
18
+ removeTool: {
19
+ id: 'Remove {title}',
20
+ defaultMessage: 'Remove {title}',
21
+ },
22
+ readyToCompare: {
23
+ id: 'Ready to compare',
24
+ defaultMessage: 'Ready to compare',
25
+ },
26
+ addAnotherTool: {
27
+ id: 'Add at least another tool',
28
+ defaultMessage: 'Add at least another tool',
29
+ },
30
+ compareSelectedTools: {
31
+ id: 'Compare selected tools',
32
+ defaultMessage: 'Compare selected tools',
33
+ },
34
+ addTool: {
35
+ id: 'Add a tool',
36
+ defaultMessage: 'Add a tool',
37
+ },
38
+ clearAll: {
39
+ id: 'Clear all',
40
+ defaultMessage: 'Clear all',
41
+ },
42
+ });
43
+
44
+ export const CompareToolsPanel = ({ appConfig: suppliedAppConfig }) => {
45
+ const intl = useIntl();
46
+ const history = useHistory();
47
+ const currentLang = useSelector((state) => state.intl.locale);
48
+ const appConfig = suppliedAppConfig || {
49
+ landingPageURL: '/en/navigator/tool-catalogue',
50
+ };
51
+ const [selectedTools, setSelectedTools] = useAtom(compareToolsAtom);
52
+ const resolvableSelectedTools = selectedTools.filter((tool) => tool.uid);
53
+ const isReadyToCompare = resolvableSelectedTools.length >= 2;
54
+ const emptySlots = MAX_COMPARE_TOOLS - selectedTools.length;
55
+
56
+ if (selectedTools.length === 0) return null;
57
+
58
+ const removeTool = (toolUid) => {
59
+ setSelectedTools((tools) => tools.filter((tool) => tool.uid !== toolUid));
60
+ };
61
+
62
+ const compareTools = () => {
63
+ const returnURL = [
64
+ window.location.pathname,
65
+ window.location.search,
66
+ window.location.hash,
67
+ ].join('');
68
+
69
+ history.push(
70
+ getCompareLocation(selectedTools, appConfig, returnURL, currentLang),
71
+ );
72
+ };
73
+
74
+ return (
75
+ <div className="catalogue-compare-panel" aria-live="polite">
76
+ <div className="compare-panel-header">
77
+ <h2>
78
+ {intl.formatMessage(messages.compareTools)}
79
+ <span className="compare-panel-count">
80
+ {selectedTools.length}/{MAX_COMPARE_TOOLS}
81
+ </span>
82
+ </h2>
83
+ </div>
84
+
85
+ <div className="compare-panel-content">
86
+ <div className="compare-panel-tools">
87
+ {selectedTools.map((tool) => (
88
+ <div key={tool.uid} className="compare-panel-tool">
89
+ <span className="navigator-tool-icon small" aria-hidden="true">
90
+ <Icon className="ri-file-line" />
91
+ </span>
92
+ <span className="compare-panel-tool-title">{tool.title}</span>
93
+ <Button
94
+ className="compare-panel-tool-clear"
95
+ aria-label={intl.formatMessage(messages.removeTool, {
96
+ title: tool.title,
97
+ })}
98
+ onClick={() => removeTool(tool.uid)}
99
+ >
100
+ <Icon className="ri-close-line" />
101
+ </Button>
102
+ </div>
103
+ ))}
104
+ {Array.from({ length: emptySlots }).map((_, index) => (
105
+ <div
106
+ key={`compare-placeholder-${index}`}
107
+ className="compare-panel-tool placeholder"
108
+ >
109
+ <span className="navigator-tool-icon small" aria-hidden="true">
110
+ <Icon className="ri-add-line" />
111
+ </span>
112
+ <span className="compare-panel-tool-title">
113
+ {intl.formatMessage(messages.addTool)}
114
+ </span>
115
+ </div>
116
+ ))}
117
+ </div>
118
+
119
+ <div className="compare-panel-actions">
120
+ <div
121
+ className={`compare-panel-status ${
122
+ isReadyToCompare ? 'ready' : 'incomplete'
123
+ }`}
124
+ >
125
+ {isReadyToCompare && <Icon className="ri-check-line" />}
126
+ {intl.formatMessage(
127
+ isReadyToCompare
128
+ ? messages.readyToCompare
129
+ : messages.addAnotherTool,
130
+ )}
131
+ </div>
132
+ <Button
133
+ primary
134
+ className="compare-panel-submit"
135
+ disabled={!isReadyToCompare}
136
+ onClick={compareTools}
137
+ >
138
+ <Icon className="ri-layout-column-line" />
139
+ <span>{intl.formatMessage(messages.compareSelectedTools)}</span>
140
+ </Button>
141
+ <Button
142
+ className="compare-panel-clear-all"
143
+ onClick={() => setSelectedTools([])}
144
+ >
145
+ {intl.formatMessage(messages.clearAll)}
146
+ </Button>
147
+ </div>
148
+ </div>
149
+ </div>
150
+ );
151
+ };
@@ -0,0 +1,96 @@
1
+ import React from 'react';
2
+ import '@testing-library/jest-dom';
3
+ import { fireEvent, render, screen } from '@testing-library/react';
4
+ import { IntlProvider } from 'react-intl';
5
+ import { Provider as JotaiProvider } from 'jotai';
6
+ import { useHistory } from 'react-router-dom';
7
+ import { CompareToolsPanel } from './CompareToolsPanel';
8
+
9
+ jest.mock('react-redux', () => ({
10
+ ...jest.requireActual('react-redux'),
11
+ useSelector: (selector) => selector({ intl: { locale: 'en' } }),
12
+ }));
13
+
14
+ jest.mock('react-router-dom', () => ({
15
+ ...jest.requireActual('react-router-dom'),
16
+ useHistory: jest.fn(),
17
+ }));
18
+
19
+ const renderPanel = (selectedTools, appConfig) => {
20
+ window.localStorage.setItem(
21
+ 'cca-compare-tools',
22
+ JSON.stringify(selectedTools),
23
+ );
24
+
25
+ return render(
26
+ <IntlProvider locale="en">
27
+ <JotaiProvider>
28
+ <CompareToolsPanel appConfig={appConfig} />
29
+ </JotaiProvider>
30
+ </IntlProvider>,
31
+ );
32
+ };
33
+
34
+ describe('CompareToolsPanel', () => {
35
+ const push = jest.fn();
36
+
37
+ beforeEach(() => {
38
+ jest.clearAllMocks();
39
+ window.localStorage.clear();
40
+ useHistory.mockReturnValue({ push });
41
+ });
42
+
43
+ it('does not render without selected tools', () => {
44
+ const { container } = renderPanel([]);
45
+
46
+ expect(container).toBeEmptyDOMElement();
47
+ });
48
+
49
+ it('shows an incomplete warning and removes the only tool', () => {
50
+ renderPanel([{ uid: 'one', title: 'Tool one' }]);
51
+
52
+ expect(screen.getByText('Add at least another tool')).toHaveClass(
53
+ 'compare-panel-status',
54
+ 'incomplete',
55
+ );
56
+ expect(
57
+ screen.getByText('Compare selected tools').closest('button'),
58
+ ).toBeDisabled();
59
+
60
+ fireEvent.click(screen.getByLabelText('Remove Tool one'));
61
+ expect(screen.queryByText('Compare tools')).not.toBeInTheDocument();
62
+ });
63
+
64
+ it('shows the ready state and opens the comparison page', () => {
65
+ renderPanel(
66
+ [
67
+ { uid: 'one', title: 'Tool one' },
68
+ { uid: 'two', title: 'Tool two' },
69
+ ],
70
+ { landingPageURL: '/en/navigator/tool-catalogue' },
71
+ );
72
+
73
+ expect(screen.getByText('Ready to compare')).toHaveClass(
74
+ 'compare-panel-status',
75
+ 'ready',
76
+ );
77
+
78
+ fireEvent.click(screen.getByText('Compare selected tools'));
79
+
80
+ expect(push).toHaveBeenCalledWith({
81
+ pathname: '/en/navigator/compare',
82
+ search: '?uid=one&uid=two',
83
+ state: { returnURL: '/' },
84
+ });
85
+ });
86
+
87
+ it('clears all selected tools', () => {
88
+ renderPanel([
89
+ { uid: 'one', title: 'Tool one' },
90
+ { uid: 'two', title: 'Tool two' },
91
+ ]);
92
+
93
+ fireEvent.click(screen.getByText('Clear all'));
94
+ expect(screen.queryByText('Compare tools')).not.toBeInTheDocument();
95
+ });
96
+ });