design-comuni-plone-theme 8.0.3 → 8.1.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.
Files changed (31) hide show
  1. package/.eslintignore +4 -0
  2. package/.eslintrc.js +41 -55
  3. package/.prettierignore +1 -0
  4. package/CHANGELOG.md +27 -0
  5. package/locales/de/LC_MESSAGES/volto.po +6 -1
  6. package/locales/en/LC_MESSAGES/volto.po +6 -1
  7. package/locales/es/LC_MESSAGES/volto.po +6 -1
  8. package/locales/fr/LC_MESSAGES/volto.po +6 -1
  9. package/locales/it/LC_MESSAGES/volto.po +7 -2
  10. package/locales/volto.pot +7 -2
  11. package/package.json +20 -11
  12. package/publiccode.yml +2 -2
  13. package/src/components/ItaliaTheme/Blocks/Accordion/Block/EditBlock.jsx +3 -3
  14. package/src/components/ItaliaTheme/Blocks/Accordion/Block/ViewBlock.jsx +3 -2
  15. package/src/components/ItaliaTheme/Blocks/VideoGallery/Body.jsx +3 -2
  16. package/src/components/ItaliaTheme/Blocks/__tests__/Accordion.test.jsx +4 -1
  17. package/src/components/ItaliaTheme/Cards/CardPersona.jsx +2 -10
  18. package/src/components/ItaliaTheme/Footer/FooterSmall.jsx +6 -18
  19. package/src/components/ItaliaTheme/View/Commons/CuredBy.jsx +4 -4
  20. package/src/components/ItaliaTheme/View/Commons/LocationsMap.jsx +7 -6
  21. package/src/components/ItaliaTheme/View/Commons/Sponsors.jsx +3 -2
  22. package/src/components/ItaliaTheme/View/ServizioView/ServizioAltriDocumenti.jsx +1 -1
  23. package/src/components/ItaliaTheme/manage/Widgets/CanaleDigitaleWidget.jsx +3 -7
  24. package/src/config/RichTextEditor/Plugins/AnchorPlugin/components/Link/index.jsx +1 -0
  25. package/src/config/italiaConfig.js +1 -0
  26. package/src/customizations/volto/components/manage/UniversalLink/UniversalLink.jsx +175 -0
  27. package/src/helpers/customizationsI18n.js +8 -0
  28. package/src/theme/ItaliaTheme/Blocks/_ctaBlock.scss +6 -0
  29. package/src/theme/ItaliaTheme/Components/_card.scss +0 -3
  30. package/src/theme/ItaliaTheme/_common.scss +17 -0
  31. package/src/theme/_site-variables.scss +2 -0
package/.eslintignore ADDED
@@ -0,0 +1,4 @@
1
+ container-query-polyfill.modern.js
2
+ cypress
3
+ node_modules
4
+ acceptance
package/.eslintrc.js CHANGED
@@ -1,59 +1,45 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- const projectRootPath = fs.realpathSync('.'); // __dirname
5
-
6
- let voltoPath = path.join(projectRootPath, 'node_modules/@plone/volto');
7
-
8
- let configFile;
9
- if (fs.existsSync(`${this.projectRootPath}/tsconfig.json`))
10
- configFile = `${this.projectRootPath}/tsconfig.json`;
11
- else if (fs.existsSync(`${this.projectRootPath}/jsconfig.json`))
12
- configFile = `${this.projectRootPath}/jsconfig.json`;
13
-
14
- if (configFile) {
15
- const jsConfig = require(configFile).compilerOptions;
16
- const pathsConfig = jsConfig.paths;
17
- if (pathsConfig['@plone/volto'])
18
- voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`;
19
- }
20
-
21
- const AddonConfigurationRegistry = require(`${voltoPath}/addon-registry.js`);
22
- const reg = new AddonConfigurationRegistry(projectRootPath);
23
-
24
- // Extends ESlint configuration for adding the aliases to `src` directories in Volto addons
25
- const addonAliases = Object.keys(reg.packages).map((o) => [
26
- o,
27
- reg.packages[o].modulePath,
28
- ]);
29
-
30
1
  module.exports = {
31
- extends: `${voltoPath}/.eslintrc`,
32
- settings: {
33
- 'import/resolver': {
34
- alias: {
35
- map: [
36
- // TODO remove the next two when implemented in core
37
- [
38
- '@plone/volto/components/theme/Image/Image',
39
- path.resolve(`${projectRootPath}/src/components/Image/Image.jsx`),
40
- ],
41
- [
42
- '@plone/volto/helpers/Image/Image',
43
- path.resolve(`${projectRootPath}/src/components/Image/helpers.js`),
44
- ],
45
-
46
- ['@plone/volto', '@plone/volto/src'],
47
- ...addonAliases,
48
- ['@package', `${__dirname}/src`],
49
- ['@root', `${__dirname}/src`],
50
- ['~', `${__dirname}/src`],
51
- ],
52
- extensions: ['.js', '.jsx', '.json'],
53
- },
54
- 'babel-plugin-root-import': {
55
- rootPathSuffix: 'src',
56
- },
2
+ extends: ['react-app', 'prettier', 'plugin:jsx-a11y/recommended'],
3
+ plugins: ['prettier', 'react-hooks', 'jsx-a11y'],
4
+ env: {
5
+ es6: true,
6
+ browser: true,
7
+ node: true,
8
+ mocha: true,
9
+ jasmine: true,
10
+ },
11
+ parser: 'babel-eslint',
12
+ parserOptions: {
13
+ ecmaVersion: 6,
14
+ sourceType: 'module',
15
+ ecmaFeatures: {
16
+ legacyDecorators: true,
57
17
  },
58
18
  },
19
+ rules: {
20
+ 'import/no-unresolved': 0,
21
+ 'no-alert': 1,
22
+ 'no-console': 1,
23
+ 'no-debugger': 1,
24
+ 'prettier/prettier': ['error', { trailingComma: 'all', singleQuote: true }],
25
+ 'react-hooks/rules-of-hooks': 'error',
26
+ 'react-hooks/exhaustive-deps': 'warn',
27
+ 'react/react-in-jsx-scope': 'off',
28
+ },
29
+ globals: {
30
+ root: true,
31
+ __DEVELOPMENT__: true,
32
+ __CLIENT__: true,
33
+ __SERVER__: true,
34
+ __DISABLE_SSR__: true,
35
+ __DEVTOOLS__: true,
36
+ __DEBUG__: true,
37
+ __SSR__: true,
38
+ __SENTRY__: true,
39
+ cy: true,
40
+ Cypress: true,
41
+ jest: true,
42
+ socket: true,
43
+ webpackIsomorphicTools: true,
44
+ },
59
45
  };
package/.prettierignore CHANGED
@@ -1,2 +1,3 @@
1
+ container-query-polyfill.modern.js
1
2
  CHANGELOG.md
2
3
  README.md
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
 
2
2
 
3
+ ## [8.1.0](https://github.com/RedTurtle/design-comuni-plone-theme/compare/v8.0.3...v8.1.0) (2023-07-20)
4
+
5
+
6
+ ### Features
7
+
8
+ * add icon to external links for accessibility, configurable in theme settings ([#250](https://github.com/RedTurtle/design-comuni-plone-theme/issues/250)) ([cecc025](https://github.com/RedTurtle/design-comuni-plone-theme/commit/cecc0258a8336c0968fa906b29ecb97a8d541749))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * a11y title in external links ([a9bd127](https://github.com/RedTurtle/design-comuni-plone-theme/commit/a9bd127f45db0af8593d4ac60fe03eba702e4038))
14
+ * card persona styles ([2f10bde](https://github.com/RedTurtle/design-comuni-plone-theme/commit/2f10bde3bd590e74a25bb09b6ae968aac1562a08))
15
+ * changed title of Altri Documenti section to Documenti ([#253](https://github.com/RedTurtle/design-comuni-plone-theme/issues/253)) ([2f84496](https://github.com/RedTurtle/design-comuni-plone-theme/commit/2f84496382dbc343bea959e6e326e9607a724670))
16
+ * error while cherry-picking changes from v2 ([8d30c6f](https://github.com/RedTurtle/design-comuni-plone-theme/commit/8d30c6f9c01bca136beb477ba1dfc1ec9c5db790))
17
+ * upgrade volto-data-grid-widget to 2.2.1 ([08bff18](https://github.com/RedTurtle/design-comuni-plone-theme/commit/08bff189d49e127545eaf98067554dd2ed8d6171))
18
+
19
+
20
+ ### Documentation
21
+
22
+ * updated publiccode ([f27748c](https://github.com/RedTurtle/design-comuni-plone-theme/commit/f27748c57d1c1dea5dec20ba73b04e095fe4e259))
23
+
24
+
25
+ ### Maintenance
26
+
27
+ * update linters config ([9c4b548](https://github.com/RedTurtle/design-comuni-plone-theme/commit/9c4b548b1dcfcda688d4b4229ad3bb339aacda91))
28
+ * updated peer dep to volto 17.0.0-alpha.19 ([50625e6](https://github.com/RedTurtle/design-comuni-plone-theme/commit/50625e6e1314201b18357fd821b82499e6f607c4))
29
+
3
30
  ## [8.0.3](https://github.com/RedTurtle/design-comuni-plone-theme/compare/v8.0.2...v8.0.3) (2023-07-14)
4
31
 
5
32
 
@@ -802,7 +802,7 @@ msgid "altre_modalita_invio"
802
802
  msgstr ""
803
803
 
804
804
  #: components/ItaliaTheme/View/ServizioView/ServizioAltriDocumenti
805
- # defaultMessage: Documenti correlati
805
+ # defaultMessage: Documenti
806
806
  msgid "altri_documenti"
807
807
  msgstr ""
808
808
 
@@ -2540,6 +2540,11 @@ msgstr ""
2540
2540
  msgid "open_end"
2541
2541
  msgstr ""
2542
2542
 
2543
+ #: helpers/customizationsI18n
2544
+ # defaultMessage: Opens in new tab
2545
+ msgid "opensInNewTab"
2546
+ msgstr ""
2547
+
2543
2548
  #: components/ItaliaTheme/Header/HeaderSearch/SearchModal
2544
2549
  # defaultMessage: Contenuto attivo
2545
2550
  msgid "optionActiveContentButton"
@@ -787,7 +787,7 @@ msgid "altre_modalita_invio"
787
787
  msgstr ""
788
788
 
789
789
  #: components/ItaliaTheme/View/ServizioView/ServizioAltriDocumenti
790
- # defaultMessage: Documenti correlati
790
+ # defaultMessage: Documenti
791
791
  msgid "altri_documenti"
792
792
  msgstr "Other documents"
793
793
 
@@ -2525,6 +2525,11 @@ msgstr "Open link in a new tab"
2525
2525
  msgid "open_end"
2526
2526
  msgstr ""
2527
2527
 
2528
+ #: helpers/customizationsI18n
2529
+ # defaultMessage: Opens in new tab
2530
+ msgid "opensInNewTab"
2531
+ msgstr "Opens in new tab"
2532
+
2528
2533
  #: components/ItaliaTheme/Header/HeaderSearch/SearchModal
2529
2534
  # defaultMessage: Contenuto attivo
2530
2535
  msgid "optionActiveContentButton"
@@ -796,7 +796,7 @@ msgid "altre_modalita_invio"
796
796
  msgstr "Otros métodos de envío"
797
797
 
798
798
  #: components/ItaliaTheme/View/ServizioView/ServizioAltriDocumenti
799
- # defaultMessage: Documenti correlati
799
+ # defaultMessage: Documenti
800
800
  msgid "altri_documenti"
801
801
  msgstr "Otros documentos"
802
802
 
@@ -2534,6 +2534,11 @@ msgstr "Abrir enlace en una pestaña nueva"
2534
2534
  msgid "open_end"
2535
2535
  msgstr "Este evento tiene una fecha de finalización abierta/variable."
2536
2536
 
2537
+ #: helpers/customizationsI18n
2538
+ # defaultMessage: Opens in new tab
2539
+ msgid "opensInNewTab"
2540
+ msgstr ""
2541
+
2537
2542
  #: components/ItaliaTheme/Header/HeaderSearch/SearchModal
2538
2543
  # defaultMessage: Contenuto attivo
2539
2544
  msgid "optionActiveContentButton"
@@ -804,7 +804,7 @@ msgid "altre_modalita_invio"
804
804
  msgstr ""
805
805
 
806
806
  #: components/ItaliaTheme/View/ServizioView/ServizioAltriDocumenti
807
- # defaultMessage: Documenti correlati
807
+ # defaultMessage: Documenti
808
808
  msgid "altri_documenti"
809
809
  msgstr "Autres documents"
810
810
 
@@ -2542,6 +2542,11 @@ msgstr "Ouvrir le lien dans un nouvel onglet"
2542
2542
  msgid "open_end"
2543
2543
  msgstr ""
2544
2544
 
2545
+ #: helpers/customizationsI18n
2546
+ # defaultMessage: Opens in new tab
2547
+ msgid "opensInNewTab"
2548
+ msgstr ""
2549
+
2545
2550
  #: components/ItaliaTheme/Header/HeaderSearch/SearchModal
2546
2551
  # defaultMessage: Contenuto attivo
2547
2552
  msgid "optionActiveContentButton"
@@ -787,9 +787,9 @@ msgid "altre_modalita_invio"
787
787
  msgstr "Altre modalità di invio"
788
788
 
789
789
  #: components/ItaliaTheme/View/ServizioView/ServizioAltriDocumenti
790
- # defaultMessage: Documenti correlati
790
+ # defaultMessage: Documenti
791
791
  msgid "altri_documenti"
792
- msgstr "Altri documenti"
792
+ msgstr "Documenti"
793
793
 
794
794
  #: config/Blocks/ListingOptions/cardWithImageTemplate
795
795
  # defaultMessage: Mostra l'immagine per tutti gli elementi
@@ -2525,6 +2525,11 @@ msgstr "Apri link in una nuova scheda"
2525
2525
  msgid "open_end"
2526
2526
  msgstr "Questo evento ha una data di fine aperta/variabile."
2527
2527
 
2528
+ #: helpers/customizationsI18n
2529
+ # defaultMessage: Opens in new tab
2530
+ msgid "opensInNewTab"
2531
+ msgstr "Si apre in una nuova scheda"
2532
+
2528
2533
  #: components/ItaliaTheme/Header/HeaderSearch/SearchModal
2529
2534
  # defaultMessage: Contenuto attivo
2530
2535
  msgid "optionActiveContentButton"
package/locales/volto.pot CHANGED
@@ -1,7 +1,7 @@
1
1
  msgid ""
2
2
  msgstr ""
3
3
  "Project-Id-Version: Plone\n"
4
- "POT-Creation-Date: 2023-06-30T12:14:35.795Z\n"
4
+ "POT-Creation-Date: 2023-07-20T15:16:26.205Z\n"
5
5
  "Last-Translator: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
6
6
  "Language-Team: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
7
7
  "MIME-Version: 1.0\n"
@@ -789,7 +789,7 @@ msgid "altre_modalita_invio"
789
789
  msgstr ""
790
790
 
791
791
  #: components/ItaliaTheme/View/ServizioView/ServizioAltriDocumenti
792
- # defaultMessage: Documenti correlati
792
+ # defaultMessage: Documenti
793
793
  msgid "altri_documenti"
794
794
  msgstr ""
795
795
 
@@ -2527,6 +2527,11 @@ msgstr ""
2527
2527
  msgid "open_end"
2528
2528
  msgstr ""
2529
2529
 
2530
+ #: helpers/customizationsI18n
2531
+ # defaultMessage: Opens in new tab
2532
+ msgid "opensInNewTab"
2533
+ msgstr ""
2534
+
2530
2535
  #: components/ItaliaTheme/Header/HeaderSearch/SearchModal
2531
2536
  # defaultMessage: Contenuto attivo
2532
2537
  msgid "optionActiveContentButton"
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "design-comuni-plone-theme",
3
3
  "description": "Volto Theme for Italia design guidelines",
4
4
  "license": "GPL-v3",
5
- "version": "8.0.3",
5
+ "version": "8.1.0",
6
6
  "main": "src/index.js",
7
7
  "keywords": [
8
8
  "volto-addon",
@@ -55,6 +55,7 @@
55
55
  },
56
56
  "stylelint": {
57
57
  "extends": [
58
+ "stylelint-config-prettier",
58
59
  "stylelint-config-idiomatic-order"
59
60
  ],
60
61
  "plugins": [
@@ -128,7 +129,7 @@
128
129
  "typeface-roboto-mono": "0.0.75",
129
130
  "typeface-titillium-web": "0.0.72",
130
131
  "volto-blocks-widget": "3.1.0",
131
- "volto-data-grid-widget": "2.2.0",
132
+ "volto-data-grid-widget": "2.2.1",
132
133
  "volto-dropdownmenu": "4.0.0",
133
134
  "volto-editablefooter": "5.0.1",
134
135
  "volto-feedback": "0.1.5",
@@ -151,19 +152,27 @@
151
152
  "@commitlint/config-conventional": "17.6.6",
152
153
  "@plone/scripts": "*",
153
154
  "@release-it/conventional-changelog": "5.1.1",
154
- "eslint": "8.42.0",
155
- "eslint-plugin-prettier": "^3.4.1",
155
+ "babel-eslint": "10.1.0",
156
+ "eslint": "6.8.0",
157
+ "eslint-config-prettier": "6.11.0",
158
+ "eslint-config-react-app": "5.2.1",
159
+ "eslint-plugin-flowtype": "4.7.0",
160
+ "eslint-plugin-import": "2.20.2",
161
+ "eslint-plugin-jsx-a11y": "6.2.3",
162
+ "eslint-plugin-prettier": "3.1.3",
163
+ "eslint-plugin-react": "7.20.0",
164
+ "eslint-plugin-react-hooks": "4.0.2",
156
165
  "husky": "8.0.2",
157
166
  "lint-staged": "13.0.3",
158
- "prettier": "^2.7.1",
159
- "release-it": "15.11.0",
160
- "stylelint": "15.10.1",
161
- "stylelint-config-idiomatic-order": "^9.0.0",
162
- "stylelint-config-prettier": "^9.0.3",
163
- "stylelint-prettier": "^2.0.0"
167
+ "prettier": "2.0.5",
168
+ "release-it": "16.1.3",
169
+ "stylelint": "15.10.2",
170
+ "stylelint-config-idiomatic-order": "9.0.0",
171
+ "stylelint-config-prettier": "9.0.5",
172
+ "stylelint-prettier": "4.0.0"
164
173
  },
165
174
  "peerDependencies": {
166
- "@plone/volto": "17.0.0-alpha.17"
175
+ "@plone/volto": "17.0.0-alpha.19"
167
176
  },
168
177
  "packageManager": "yarn@3.2.3"
169
178
  }
package/publiccode.yml CHANGED
@@ -227,9 +227,9 @@ maintenance:
227
227
  name: io-Comune - Il sito AgID per Comuni ed Enti Pubblici
228
228
  platforms:
229
229
  - web
230
- releaseDate: '2023-07-14'
230
+ releaseDate: '2023-07-20'
231
231
  softwareType: standalone/web
232
- softwareVersion: 8.0.3
232
+ softwareVersion: 8.0.4
233
233
  url: 'https://github.com/italia/design-comuni-plone-theme'
234
234
  usedBy:
235
235
  - ASP Comuni Modenesi Area Nord
@@ -8,7 +8,7 @@ import { compose } from 'redux';
8
8
  import { injectIntl, defineMessages } from 'react-intl';
9
9
 
10
10
  import { injectDNDSubblocks, SubblockEdit, Subblock } from 'volto-subblocks';
11
-
11
+ import { UniversalLink } from '@plone/volto/components';
12
12
  import { Button } from 'design-react-kit';
13
13
  import { Icon } from 'design-comuni-plone-theme/components/ItaliaTheme';
14
14
 
@@ -146,11 +146,11 @@ class EditBlock extends SubblockEdit {
146
146
  </div>
147
147
  {this.props.data.href && (
148
148
  <div className="link-more">
149
- <a href={this.props.data.href}>
149
+ <UniversalLink href={this.props.data.href}>
150
150
  {this.props.data.linkMoreTitle ||
151
151
  this.props.intl.formatMessage(messages.vedi)}
152
152
  <Icon icon="it-arrow-right" />
153
- </a>
153
+ </UniversalLink>
154
154
  </div>
155
155
  )}
156
156
  </div>
@@ -9,6 +9,7 @@ import redraft from 'redraft';
9
9
  import { useIntl, defineMessages } from 'react-intl';
10
10
  import cx from 'classnames';
11
11
  import { Icon } from 'design-comuni-plone-theme/components/ItaliaTheme';
12
+ import { UniversalLink } from '@plone/volto/components';
12
13
  import config from '@plone/volto/registry';
13
14
 
14
15
  const messages = defineMessages({
@@ -66,10 +67,10 @@ const ViewBlock = ({ data, isOpen, toggle, id, index }) => {
66
67
  </div>
67
68
  {data.href && (
68
69
  <div className="link-more">
69
- <a href={data.href}>
70
+ <UniversalLink href={data.href}>
70
71
  {data.linkMoreTitle || intl.formatMessage(messages.vedi)}
71
72
  <Icon icon="it-arrow-right" />
72
- </a>
73
+ </UniversalLink>
73
74
  </div>
74
75
  )}
75
76
  </div>
@@ -10,6 +10,7 @@ import { Container } from 'design-react-kit';
10
10
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
11
11
 
12
12
  import { Icon } from 'design-comuni-plone-theme/components/ItaliaTheme';
13
+ import { UniversalLink } from '@plone/volto/components';
13
14
 
14
15
  /**
15
16
  * View Video Gallery body class
@@ -61,13 +62,13 @@ const Body = ({ data, children, nItems = 0, reactSlick }) => {
61
62
  <div className="channel">
62
63
  <Icon color="primary" icon="it-youtube" className="me-2" />
63
64
  {data.channel_link ? (
64
- <a
65
+ <UniversalLink
65
66
  href={data.channel_link}
66
67
  rel="noopener noreferrer"
67
68
  target="_blank"
68
69
  >
69
70
  {data.channel_link_title || data.channel_link}
70
- </a>
71
+ </UniversalLink>
71
72
  ) : (
72
73
  <span>{data.channel_link_title}</span>
73
74
  )}
@@ -5,6 +5,7 @@ import AccordionView from '../Accordion/View';
5
5
  import configureStore from 'redux-mock-store';
6
6
  import { Provider } from 'react-intl-redux';
7
7
  import thunk from 'redux-thunk';
8
+ import { MemoryRouter } from 'react-router-dom';
8
9
 
9
10
  const middlewares = [thunk];
10
11
  const mockStore = configureStore(middlewares);
@@ -59,7 +60,9 @@ const store = mockStore({
59
60
  test('View renders all fields', async () => {
60
61
  render(
61
62
  <Provider store={store}>
62
- <AccordionView data={mock_fields} />
63
+ <MemoryRouter>
64
+ <AccordionView data={mock_fields} />
65
+ </MemoryRouter>
63
66
  </Provider>,
64
67
  );
65
68
 
@@ -26,7 +26,7 @@ export const CardPersona = ({
26
26
  useOriginal: false,
27
27
  });
28
28
 
29
- const hasImage = image !== null;
29
+ const hasImage = image !== null && showImage;
30
30
 
31
31
  return (
32
32
  <Card
@@ -53,15 +53,7 @@ export const CardPersona = ({
53
53
  {item.title || item.id}
54
54
  </UniversalLink>
55
55
  </CardTitle>
56
- {listingText && (
57
- <CardText
58
- className={cx('', {
59
- 'card-with-picture': !!image,
60
- })}
61
- >
62
- {listingText}
63
- </CardText>
64
- )}
56
+ {listingText && <CardText>{listingText}</CardText>}
65
57
  </CardBody>
66
58
  {showImage && (
67
59
  <div className="card-image card-image-rounded">{image}</div>
@@ -3,7 +3,7 @@
3
3
  * @module components/theme/Footer/FooterSmall
4
4
  */
5
5
 
6
- import React, { useState, useEffect } from 'react';
6
+ import React, { useEffect } from 'react';
7
7
  import cx from 'classnames';
8
8
  import { UniversalLink } from '@plone/volto/components';
9
9
  import { defineMessages, useIntl } from 'react-intl';
@@ -35,22 +35,17 @@ const FooterSmall = () => {
35
35
  const intl = useIntl();
36
36
  const pathname = useLocation().pathname;
37
37
  const dispatch = useDispatch();
38
- const [links, setLinks] = useState([]);
39
38
 
40
39
  const subFooter = useSelector((state) => state.subFooter?.result);
41
40
  const subFooterItems = getItemsByPath(subFooter, pathname)?.filter(
42
41
  (item) => item.visible,
43
42
  );
44
43
 
45
- useEffect(() => {
46
- dispatch(getSubFooter());
47
- // eslint-disable-next-line react-hooks/exhaustive-deps
48
- }, []);
44
+ const links = getSiteProperty('smallFooterLinks', intl.locale) ?? [];
49
45
 
50
46
  useEffect(() => {
51
- let _links = getSiteProperty('smallFooterLinks', intl.locale) ?? [];
52
- setLinks(_links);
53
- }, [intl.locale]);
47
+ dispatch(getSubFooter());
48
+ }, [dispatch]);
54
49
 
55
50
  return subFooterItems?.length > 0 || links.length > 0 || true ? (
56
51
  <div className="it-footer-small-prints clearfix">
@@ -70,11 +65,7 @@ const FooterSmall = () => {
70
65
  })}
71
66
  key={url + index}
72
67
  >
73
- <UniversalLink
74
- data-element={item.id_lighthouse}
75
- href={url}
76
- title={item.title}
77
- >
68
+ <UniversalLink data-element={item.id_lighthouse} href={url}>
78
69
  {item.title}
79
70
  </UniversalLink>
80
71
  </li>
@@ -83,9 +74,7 @@ const FooterSmall = () => {
83
74
  {links?.length > 0 &&
84
75
  links.map((link) => (
85
76
  <li className="list-inline-item" key={link.url}>
86
- <UniversalLink href={link.url} title={link.title}>
87
- {link.title}
88
- </UniversalLink>
77
+ <UniversalLink href={link.url}>{link.title}</UniversalLink>
89
78
  </li>
90
79
  ))}
91
80
  <li className="list-inline-item">
@@ -95,7 +84,6 @@ const FooterSmall = () => {
95
84
  e.preventDefault();
96
85
  dispatch(displayBanner(true, true));
97
86
  }}
98
- title={intl.formatMessage(messages.cookieSettings)}
99
87
  >
100
88
  {intl.formatMessage(messages.cookieSettings)}
101
89
  </button>
@@ -2,8 +2,8 @@ import { defineMessages, useIntl } from 'react-intl';
2
2
  import React from 'react';
3
3
  import PropTypes from 'prop-types';
4
4
  import { Chip, ChipLabel } from 'design-react-kit';
5
- import { Link } from 'react-router-dom';
6
5
  import { flattenToAppURL } from '@plone/volto/helpers';
6
+ import { UniversalLink } from '@plone/volto/components';
7
7
  import {
8
8
  OfficeCard,
9
9
  RichTextSection,
@@ -57,8 +57,8 @@ const CuredBy = ({ office, people, title }) => {
57
57
  </h5>
58
58
  )}
59
59
  {people.map((item, i) => (
60
- <Link
61
- to={flattenToAppURL(item['@id'])}
60
+ <UniversalLink
61
+ href={flattenToAppURL(item['@id'])}
62
62
  key={item['@id']}
63
63
  className="text-decoration-none me-2"
64
64
  >
@@ -71,7 +71,7 @@ const CuredBy = ({ office, people, title }) => {
71
71
  >
72
72
  <ChipLabel tag="span">{item.title}</ChipLabel>
73
73
  </Chip>
74
- </Link>
74
+ </UniversalLink>
75
75
  ))}
76
76
  </div>
77
77
  ) : null}
@@ -5,6 +5,7 @@ import { getContent, resetContent } from '@plone/volto/actions';
5
5
  import { flattenToAppURL } from '@plone/volto/helpers';
6
6
  import { OSMMap } from 'volto-venue';
7
7
  import PropTypes from 'prop-types';
8
+ import { UniversalLink } from '@plone/volto/components';
8
9
 
9
10
  /**
10
11
  * LocationsMap view component class.
@@ -62,7 +63,7 @@ const LocationsMap = ({ center, locations }) => {
62
63
  <div className="map-pin-popup">
63
64
  <div className="title">{item.title}</div>
64
65
  <p>
65
- <a
66
+ <UniversalLink
66
67
  href={`http://maps.google.com/?q=${item.street ?? ''} ${
67
68
  item.zip_code ?? ''
68
69
  } ${item.city ?? ''} ${item.province ?? ''} ${
@@ -72,10 +73,10 @@ const LocationsMap = ({ center, locations }) => {
72
73
  rel="noopener noreferrer"
73
74
  >
74
75
  {intl.formatMessage(messages.view_on_googlemaps)}
75
- </a>
76
+ </UniversalLink>
76
77
  </p>
77
78
  <p>
78
- <a
79
+ <UniversalLink
79
80
  href={`https://bing.com/maps/default.aspx?where1=${
80
81
  item.street ?? ''
81
82
  } ${item.zip_code ?? ''} ${item.city ?? ''} ${item.province ?? ''}`}
@@ -83,10 +84,10 @@ const LocationsMap = ({ center, locations }) => {
83
84
  rel="noopener noreferrer"
84
85
  >
85
86
  {intl.formatMessage(messages.view_on_bingmaps)}
86
- </a>
87
+ </UniversalLink>
87
88
  </p>
88
89
  <p>
89
- <a
90
+ <UniversalLink
90
91
  href={` http://maps.apple.com/?q=${item.street ?? ''} ${
91
92
  item.zip_code ?? ''
92
93
  } ${item.city ?? ''} ${item.province ?? ''}`}
@@ -94,7 +95,7 @@ const LocationsMap = ({ center, locations }) => {
94
95
  rel="noopener noreferrer"
95
96
  >
96
97
  {intl.formatMessage(messages.view_on_applemaps)}
97
- </a>
98
+ </UniversalLink>
98
99
  </p>
99
100
  </div>
100
101
  );
@@ -5,6 +5,7 @@ import { searchContent, resetSearchContent } from '@plone/volto/actions';
5
5
  import { flattenToAppURL } from '@plone/volto/helpers';
6
6
  import PropTypes from 'prop-types';
7
7
  import Image from '@plone/volto/components/theme/Image/Image';
8
+ import { UniversalLink } from '@plone/volto/components';
8
9
 
9
10
  const messages = defineMessages({
10
11
  sponsors: {
@@ -44,14 +45,14 @@ const Sponsor = ({ item }) => {
44
45
  </div>
45
46
  ) : (
46
47
  <div className="sponsor-item">
47
- <a
48
+ <UniversalLink
48
49
  href={item.remoteUrl}
49
50
  alt=""
50
51
  target="_blank"
51
52
  rel="noopener noreferrer"
52
53
  >
53
54
  {item.title}
54
- </a>
55
+ </UniversalLink>
55
56
  </div>
56
57
  )}
57
58
  </>
@@ -10,7 +10,7 @@ import {
10
10
  const messages = defineMessages({
11
11
  altri_documenti: {
12
12
  id: 'altri_documenti',
13
- defaultMessage: 'Documenti correlati',
13
+ defaultMessage: 'Documenti',
14
14
  },
15
15
  });
16
16
 
@@ -3,6 +3,7 @@ import { Form } from 'semantic-ui-react';
3
3
  import config from '@plone/volto/registry';
4
4
  import cx from 'classnames';
5
5
  import { defineMessages, injectIntl } from 'react-intl';
6
+ import { UniversalLink } from '@plone/volto/components';
6
7
 
7
8
  const messages = defineMessages({
8
9
  canale_digitale_widget_title: {
@@ -33,14 +34,9 @@ const CanaleDigitaleWidget = (props) => {
33
34
  </h3>
34
35
  <p className="help">
35
36
  Sezione obbligatoria ai fini della validazione AGID secondo i seguenti{' '}
36
- <a
37
- href="https://docs.italia.it/italia/designers-italia/app-valutazione-modelli-docs/it/versione-attuale/requisiti-e-modalita-verifica-comuni.html"
38
- target="_blank"
39
- noopener
40
- noreferrer
41
- >
37
+ <UniversalLink href="https://docs.italia.it/italia/designers-italia/app-valutazione-modelli-docs/it/versione-attuale/requisiti-e-modalita-verifica-comuni.html">
42
38
  termini
43
- </a>
39
+ </UniversalLink>
44
40
  . Per confermare la presenza in pagina della relativa sezione devono
45
41
  essere presenti:
46
42
  <ul>
@@ -2,6 +2,7 @@
2
2
  * Questo è il link quando viene mostrato dentro l'editor nelle viste di edit
3
3
  * Customizzato
4
4
  * - aggiunto data-element
5
+ * - aggiunto icona link esterno
5
6
  */
6
7
  import React from 'react';
7
8
  import PropTypes from 'prop-types';
@@ -236,6 +236,7 @@ export default function applyConfig(voltoConfig) {
236
236
  enableVoltoFormBlockCaptcha: true,
237
237
  splitMegamenuColumns: true, //se impostato a false, non spezza le colonne con intestazioni nel megamenu
238
238
  footerNavigationDepth: 2, //valori possibili: [1,2]. Se impostato ad 1 non verranno mostrati nel footer i link agli elementi contenuti nelle sezioni di primo livello.
239
+ markSpecialLinks: true, // se impostato a false, non marca con icona i link esterni
239
240
  },
240
241
  apiExpanders: [
241
242
  ...config.settings.apiExpanders,
@@ -0,0 +1,175 @@
1
+ /*
2
+ * UniversalLink
3
+ * @module components/UniversalLink
4
+ *
5
+ * CUSTOMIZATIONS:
6
+ * - aggiunto icona per link esterni
7
+ * - aggiunto title informativo per link esterni
8
+ */
9
+
10
+ import React from 'react';
11
+ import PropTypes from 'prop-types';
12
+ import { useIntl } from 'react-intl';
13
+ import { HashLink as Link } from 'react-router-hash-link';
14
+ import { useSelector } from 'react-redux';
15
+ import {
16
+ flattenToAppURL,
17
+ isInternalURL,
18
+ URLUtils,
19
+ } from '@plone/volto/helpers/Url/Url';
20
+ import { matchPath } from 'react-router';
21
+ import { Icon } from 'design-comuni-plone-theme/components/ItaliaTheme';
22
+
23
+ import config from '@plone/volto/registry';
24
+
25
+ const UniversalLink = ({
26
+ href,
27
+ item = null,
28
+ openLinkInNewTab,
29
+ download = false,
30
+ children,
31
+ className = null,
32
+ title = null,
33
+ ...props
34
+ }) => {
35
+ const intl = useIntl();
36
+ const token = useSelector((state) => state.userSession?.token);
37
+
38
+ let url = href;
39
+ if (!href && item) {
40
+ if (!item['@id']) {
41
+ // eslint-disable-next-line no-console
42
+ console.error(
43
+ 'Invalid item passed to UniversalLink',
44
+ item,
45
+ props,
46
+ children,
47
+ );
48
+ url = '#';
49
+ } else {
50
+ //case: generic item
51
+ url = flattenToAppURL(item['@id']);
52
+
53
+ //case: item like a Link
54
+ let remoteUrl = item.remoteUrl || item.getRemoteUrl;
55
+ if (!token && remoteUrl) {
56
+ url = remoteUrl;
57
+ }
58
+
59
+ //case: item of type 'File'
60
+ if (
61
+ !token &&
62
+ config.settings.downloadableObjects.includes(item['@type'])
63
+ ) {
64
+ url = `${url}/@@download/file`;
65
+ }
66
+
67
+ if (
68
+ !token &&
69
+ config.settings.viewableInBrowserObjects.includes(item['@type'])
70
+ ) {
71
+ url = `${url}/@@display-file/file`;
72
+ }
73
+ }
74
+ }
75
+
76
+ const isBlacklisted =
77
+ (config.settings.externalRoutes ?? []).find((route) =>
78
+ matchPath(flattenToAppURL(url), route.match),
79
+ )?.length > 0;
80
+ const isExternal = !isInternalURL(url) || isBlacklisted;
81
+ const isDownload = (!isExternal && url.includes('@@download')) || download;
82
+ const isDisplayFile =
83
+ (!isExternal && url.includes('@@display-file')) || false;
84
+
85
+ const checkedURL = URLUtils.checkAndNormalizeUrl(url);
86
+
87
+ url = checkedURL.url;
88
+ let tag = (
89
+ <Link
90
+ to={flattenToAppURL(url)}
91
+ target={openLinkInNewTab ?? false ? '_blank' : null}
92
+ title={title}
93
+ className={className}
94
+ smooth={config.settings.hashLinkSmoothScroll}
95
+ {...props}
96
+ >
97
+ {children}
98
+ </Link>
99
+ );
100
+
101
+ if (isExternal) {
102
+ tag = (
103
+ <a
104
+ href={url}
105
+ title={`${title ? title + ' - ' : ''}${intl.formatMessage({
106
+ id: 'opensInNewTab',
107
+ })}`}
108
+ target={
109
+ !checkedURL.isMail &&
110
+ !checkedURL.isTelephone &&
111
+ !(openLinkInNewTab === false)
112
+ ? '_blank'
113
+ : null
114
+ }
115
+ rel="noopener noreferrer"
116
+ className={className}
117
+ {...props}
118
+ >
119
+ {children}
120
+ {config.settings.siteProperties.markSpecialLinks && (
121
+ <Icon
122
+ icon="it-external-link"
123
+ title={title}
124
+ size="xs"
125
+ className="align-top ms-1 external-link"
126
+ />
127
+ )}
128
+ </a>
129
+ );
130
+ } else if (isDownload) {
131
+ tag = (
132
+ <a
133
+ href={flattenToAppURL(url)}
134
+ download
135
+ title={title}
136
+ className={className}
137
+ {...props}
138
+ >
139
+ {children}
140
+ </a>
141
+ );
142
+ } else if (isDisplayFile) {
143
+ tag = (
144
+ <a
145
+ href={flattenToAppURL(url)}
146
+ title={title}
147
+ target="_blank"
148
+ rel="noopener noreferrer"
149
+ className={className}
150
+ {...props}
151
+ >
152
+ {children}
153
+ </a>
154
+ );
155
+ }
156
+ return tag;
157
+ };
158
+
159
+ UniversalLink.propTypes = {
160
+ href: PropTypes.string,
161
+ openLinkInNewTab: PropTypes.bool,
162
+ download: PropTypes.bool,
163
+ className: PropTypes.string,
164
+ title: PropTypes.string,
165
+ item: PropTypes.shape({
166
+ '@id': PropTypes.string.isRequired,
167
+ remoteUrl: PropTypes.string, //of plone @type 'Link'
168
+ }),
169
+ children: PropTypes.oneOfType([
170
+ PropTypes.arrayOf(PropTypes.node),
171
+ PropTypes.node,
172
+ ]),
173
+ };
174
+
175
+ export default UniversalLink;
@@ -0,0 +1,8 @@
1
+ import { defineMessages } from 'react-intl';
2
+
3
+ defineMessages({
4
+ opensInNewTab: {
5
+ id: 'opensInNewTab',
6
+ defaultMessage: 'Opens in new tab',
7
+ },
8
+ });
@@ -41,6 +41,12 @@
41
41
  &:focus {
42
42
  background: $secondary-text;
43
43
  color: $secondary;
44
+ .external-link {
45
+ fill: #004080 !important;
46
+ }
47
+ }
48
+ .external-link {
49
+ fill: $external-link-fill-buttons !important;
44
50
  }
45
51
  }
46
52
 
@@ -96,9 +96,6 @@
96
96
  @include rem-size(font-size, 18);
97
97
  @include rem-size(line-height, 24);
98
98
  font-family: 'Titillium Web' !important;
99
- &.card-with-picture {
100
- width: 60%;
101
- }
102
99
  }
103
100
  }
104
101
  }
@@ -43,4 +43,21 @@
43
43
  }
44
44
  }
45
45
  }
46
+ .btn-primary,
47
+ .btn-secondary,
48
+ .btn-tertiary,
49
+ .draftjs-buttons {
50
+ .external-link {
51
+ fill: $external-link-fill-buttons !important;
52
+ }
53
+ }
54
+ .external-link {
55
+ fill: $link-color !important;
56
+ &:hover {
57
+ fill: #004080 !important;
58
+ }
59
+ }
60
+ .it-footer-small-prints-list .external-link {
61
+ fill: $external-link-fill-subfooter !important;
62
+ }
46
63
  }
@@ -45,6 +45,8 @@ $primary-a0: hsl($primary-h, 62%, 97%) !default;
45
45
 
46
46
  $argument-icon-color: $tertiary !default;
47
47
  $argument-icon-bg: #f5f6f7 !default;
48
+ $external-link-fill-buttons: $tertiary-text !default;
49
+ $external-link-fill-subfooter: $tertiary-text !default;
48
50
 
49
51
  // $dvt-header-center-max-height: 166px;
50
52
  // $header-center-max-height-mob: 80px;